diff --git a/test/integration/__init__.py b/test/integration/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/integration/component/README b/test/integration/component/README new file mode 100644 index 00000000000..2fd8d4be795 --- /dev/null +++ b/test/integration/component/README @@ -0,0 +1,41 @@ +P1 Cases +-------------------------------------- +These test cases are the core functionality tests that ensure the application is stable and can be tested thoroughly. +These P1 cases definitions are located at : https://docs.google.com/a/clogeny.com/spreadsheet/ccc?key=0Aq5M2ldK6eyedDJBa0EzM0RPNmdVNVZOWnFnOVJJcHc&hl=en_US + + +Guidelines +---------- +P1 test cases are being developed using Python's unittests2. Following are certain guidelines being followed + 1. Tests exercised for the same resource should ideally be present under a single suite or file. + + 2. Time-consuming operations that create new cloud resources like server creation, volume creation etc + should not necessarily be exercised per unit test. The resources can be shared by creating them at + the class-level using setUpClass and shared across all instances during a single run. + + 3. Certain tests pertaining to NAT, Firewall and Load Balancing warrant fresh resources per test. Hence a call should be + taken by the stakeholders regarding sharing resources. + + 4. Ensure that the tearDown/tearDownClass functions clean up all the resources created during the test run. + +For more information about unittests: http://docs.python.org/library/unittest.html + + +P1 Tests +---------- +The following files contain these P1 cases: + +1. test_snapshots.py - Snapshots related tests +2. test_routers.py - Router related tests +3. test_usage.py - Usage realted tests +4. test_account.py - Account related tests +5. test_resource_limits.py - Resource limits tests +6. test_security_groups.py - Security groups related tests +7. test_templates.py - templates related tests +8. test_volumes.py - Volumes related tests +9. test_blocker_bugs.py - Blocker bugs tests +10. test_project_configs.py - Project global configuration related tests +11. test_project_limits.py - Project resource limits related tests +12. test_project_resources.py - Project resource creation related tests +13. test_project_usage.py - Project usage related tests +14. test_projects - Projects functionality tests diff --git a/test/integration/component/__init__.py b/test/integration/component/__init__.py new file mode 100644 index 00000000000..d65b5e89b69 --- /dev/null +++ b/test/integration/component/__init__.py @@ -0,0 +1,13 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 \ No newline at end of file diff --git a/test/integration/component/test_accounts.py b/test/integration/component/test_accounts.py new file mode 100644 index 00000000000..19f6914229f --- /dev/null +++ b/test/integration/component/test_accounts.py @@ -0,0 +1,1091 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Account +""" +#Import Local Modules +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +import datetime + + +class Services: + """Test Account Services + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "user": { + "email": "user@test.com", + "firstname": "User", + "lastname": "User", + "username": "User", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "template": { + "displaytext": "Public Template", + "name": "Public template", + "ostypeid": 'aaf6e8c9-b609-441d-9ebd-b4eaa030a275', + "url": "http://download.cloud.com/releases/2.0.0/UbuntuServer-10-04-64bit.vhd.bz2", + "hypervisor": 'XenServer', + "format" : 'VHD', + "isfeatured": True, + "ispublic": True, + "isextractable": True, + }, + "ostypeid": 'aaf6e8c9-b609-441d-9ebd-b4eaa030a275', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode":'advanced' + } + + +class TestAccounts(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestAccounts, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup = [cls.service_offering] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_create_account(self): + """Test Create Account and user for that account + """ + + # Validate the following + # 1. Create an Account. Verify the account is created. + # 2. Create User associated with that account. Verify the created user + + # Create an account + account = Account.create( + self.apiclient, + self.services["account"] + ) + self.debug("Created account: %s" % account.account.name) + self.cleanup.append(account) + list_accounts_response = list_accounts( + self.apiclient, + id=account.account.id + ) + self.assertEqual( + isinstance(list_accounts_response, list), + True, + "Check list accounts for valid data" + ) + self.assertNotEqual( + len(list_accounts_response), + 0, + "Check List Account response" + ) + + account_response = list_accounts_response[0] + self.assertEqual( + account.account.accounttype, + account_response.accounttype, + "Check Account Type of Created account" + ) + self.assertEqual( + account.account.name, + account_response.name, + "Check Account Name of Created account" + ) + # Create an User associated with account + user = User.create( + self.apiclient, + self.services["user"], + account=account.account.name, + domainid=account.account.domainid + ) + self.debug("Created user: %s" % user.id) + list_users_response = list_users( + self.apiclient, + id=user.id + ) + self.assertEqual( + isinstance(list_users_response, list), + True, + "Check list users for valid data" + ) + + self.assertNotEqual( + len(list_users_response), + 0, + "Check List User response" + ) + + user_response = list_users_response[0] + self.assertEqual( + user.username, + user_response.username, + "Check username of Created user" + ) + self.assertEqual( + user.state, + user_response.state, + "Check state of created user" + ) + return + + +class TestRemoveUserFromAccount(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestRemoveUserFromAccount, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + # Create an account + cls.account = Account.create( + cls.api_client, + cls.services["account"] + ) + + cls._cleanup = [ + cls.service_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, users etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_user_remove_VM_running(self): + """Test Remove one user from the account + """ + + # Validate the following + # 1. Create an account with 2 users. + # 2. Start 2 VMs; one for each user of the account + # 3. Remove one user from the account. Verify that account still exists. + # 4. Verify that VM started by the removed user are still running + + # Create an User associated with account and VMs + user_1 = User.create( + self.apiclient, + self.services["user"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created user: %s" % user_1.id) + user_2 = User.create( + self.apiclient, + self.services["user"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created user: %s" % user_2.id) + self.cleanup.append(user_2) + + vm_1 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM in account: %s, ID: %s" % ( + self.account.account.name, + vm_1.id + )) + self.cleanup.append(vm_1) + + vm_2 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM in account: %s, ID: %s" % ( + self.account.account.name, + vm_2.id + )) + self.cleanup.append(vm_2) + + # Remove one of the user + self.debug("Deleting user: %s" % user_1.id) + user_1.delete(self.apiclient) + + # Account should exist after deleting user + accounts_response = list_accounts( + self.apiclient, + id=self.account.account.id + ) + self.assertEqual( + isinstance(accounts_response, list), + True, + "Check for valid list accounts response" + ) + + self.assertNotEqual( + len(accounts_response), + 0, + "Check List Account response" + ) + vm_response = list_virtual_machines( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(vm_response, list), + True, + "Check for valid list VM response" + ) + + self.assertNotEqual( + len(vm_response), + 0, + "Check List VM response" + ) + + # VMs associated with that account should be running + for vm in vm_response: + self.assertEqual( + vm.state, + 'Running', + "Check state of VMs associated with account" + ) + return + @unittest.skip("Open Questions") + def test_02_remove_all_users(self): + """Test Remove both users from the account + """ + + # Validate the following + # 1. Remove both the users from the account. + # 2. Verify account is removed + # 3. Verify all VMs associated with that account got removed + + # Create an User associated with account and VMs + user_1 = User.create( + self.apiclient, + self.services["user"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created user: %s" % user_1.id) + user_2 = User.create( + self.apiclient, + self.services["user"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created user: %s" % user_2.id) + vm_1 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM in account: %s, ID: %s" % ( + self.account.account.name, + vm_1.id + )) + vm_2 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM in account: %s, ID: %s" % ( + self.account.account.name, + vm_2.id + )) + # Get users associated with an account + # (Total 3: 2 - Created & 1 default generated while account creation) + users = list_users( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(users, list), + True, + "Check for valid list users response" + ) + for user in users: + + self.debug("Deleting user: %s" % user.id) + cmd = deleteUser.deleteUserCmd() + cmd.id = user.id + self.apiclient.deleteUser(cmd) + + interval = list_configurations( + self.apiclient, + name='account.cleanup.interval' + ) + self.assertEqual( + isinstance(interval, list), + True, + "Check for valid list configurations response" + ) + self.debug("account.cleanup.interval: %s" % interval[0].value) + + # Sleep to ensure that all resources are deleted + time.sleep(int(interval[0].value)) + + # Account is removed after last user is deleted + account_response = list_accounts( + self.apiclient, + id=self.account.account.id + ) + self.assertEqual( + account_response, + None, + "Check List VM response" + ) + # All VMs associated with account are removed. + vm_response = list_virtual_machines( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + vm_response, + None, + "Check List VM response" + ) + # DomR associated with account is deleted + with self.assertRaises(Exception): + list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + return + + +class TestNonRootAdminsPrivileges(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestNonRootAdminsPrivileges, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone settings + cls.zone = get_zone(cls.api_client, cls.services) + + # Create an account, domain etc + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"], + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup = [ + cls.account, + cls.domain + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_non_root_admin_Privileges(self): + """Test to verify Non Root admin previleges""" + + # Validate the following + # 1. Create few accounts/users in ROOT domain + # 2. Verify listAccounts API gives only accounts associated with new + # domain. + + # Create accounts for ROOT domain + account_1 = Account.create( + self.apiclient, + self.services["account"] + ) + self.debug("Created account: %s" % account_1.account.name) + self.cleanup.append(account_1) + account_2 = Account.create( + self.apiclient, + self.services["account"] + ) + self.debug("Created account: %s" % account_2.account.name) + self.cleanup.append(account_2) + + accounts_response = list_accounts( + self.apiclient, + domainid=self.domain.id + ) + + self.assertEqual( + isinstance(accounts_response, list), + True, + "Check list accounts response for valid data" + ) + + self.assertEqual( + len(accounts_response), + 1, + "Check List accounts response" + ) + # Verify only account associated with domain is listed + for account in accounts_response: + self.assertEqual( + account.domainid, + self.domain.id, + "Check domain ID of account" + ) + return + + +class TestServiceOfferingSiblings(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestServiceOfferingSiblings, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Create Domains, accounts etc + cls.domain_1 = Domain.create( + cls.api_client, + cls.services["domain"] + ) + cls.domain_2 = Domain.create( + cls.api_client, + cls.services["domain"] + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.domain_1.id + ) + # Create account for doamin_1 + cls.account_1 = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain_1.id + ) + + # Create an account for domain_2 + cls.account_2 = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain_2.id + ) + + cls._cleanup = [ + cls.account_1, + cls.account_2, + cls.service_offering, + cls.domain_1, + cls.domain_2, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created domains, accounts + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_service_offering_siblings(self): + """Test to verify service offerings at same level in hierarchy""" + + # Validate the following + # 1. Verify service offering is visible for domain_1 + # 2. Verify service offering is not visible for domain_2 + + service_offerings = list_service_offering( + self.apiclient, + domainid=self.domain_1.id + ) + self.assertEqual( + isinstance(service_offerings, list), + True, + "Check if valid list service offerings response" + ) + + self.assertNotEqual( + len(service_offerings), + 0, + "Check List Service Offerings response" + ) + + for service_offering in service_offerings: + self.debug("Validating service offering: %s" % service_offering.id) + self.assertEqual( + service_offering.id, + self.service_offering.id, + "Check Service offering ID for domain" + str(self.domain_1.name) + ) + # Verify private service offering is not visible to other domain + service_offerings = list_service_offering( + self.apiclient, + domainid=self.domain_2.id + ) + self.assertEqual( + service_offerings, + None, + "Check List Service Offerings response for other domain" + ) + return + +@unittest.skip("Open Questions") +class TestServiceOfferingHierarchy(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestServiceOfferingHierarchy, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Create domain, service offerings etc + cls.domain_1 = Domain.create( + cls.api_client, + cls.services["domain"] + ) + cls.domain_2 = Domain.create( + cls.api_client, + cls.services["domain"], + parentdomainid=cls.domain_1.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.domain_1.id + ) + # Create account for doamin_1 + cls.account_1 = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain_1.id + ) + + # Create an account for domain_2 + cls.account_2 = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain_2.id + ) + + cls._cleanup = [ + cls.account_1, + cls.account_2, + cls.service_offering, + cls.domain_1, + cls.domain_2, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_service_offering_hierarchy(self): + """Test to verify service offerings at same level in hierarchy""" + + # Validate the following + # 1. Verify service offering is visible for domain_1 + # 2. Verify service offering is also visible for domain_2 + + service_offerings = list_service_offering( + self.apiclient, + domainid=self.domain_1.id + ) + self.assertEqual( + isinstance(service_offerings, list), + True, + "Check List Service Offerings for a valid response" + ) + self.assertNotEqual( + len(service_offerings), + 0, + "Check List Service Offerings response" + ) + + for service_offering in service_offerings: + self.assertEqual( + service_offering.id, + self.service_offering.id, + "Check Service offering ID for domain" + str(self.domain_1.name) + ) + + # Verify private service offering is not visible to other domain + service_offerings = list_service_offering( + self.apiclient, + domainid=self.domain_2.id + ) + self.assertEqual( + isinstance(service_offerings, list), + True, + "Check List Service Offerings for a valid response" + ) + self.assertNotEqual( + len(service_offerings), + 0, + "Check List Service Offerings response" + ) + + for service_offering in service_offerings: + self.assertEqual( + service_offering.id, + self.service_offering.id, + "Check Service offering ID for domain" + str(self.domain_2.name) + ) + return + +@unittest.skip("Open Questions") +class TesttemplateHierarchy(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TesttemplateHierarchy, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone settings + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["template"]["zoneid"] = cls.zone.id + + # Create domains, accounts and template + cls.domain_1 = Domain.create( + cls.api_client, + cls.services["domain"] + ) + cls.domain_2 = Domain.create( + cls.api_client, + cls.services["domain"], + parentdomainid=cls.domain_1.id + ) + + # Create account for doamin_1 + cls.account_1 = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain_1.id + ) + + # Create an account for domain_2 + cls.account_2 = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain_2.id + ) + + cls.template = Template.register( + cls.api_client, + cls.services["template"], + account=cls.account_1.account.name, + domainid=cls.domain_1.id + ) + cls._cleanup = [ + cls.template, + cls.account_1, + cls.account_2, + cls.domain_1, + cls.domain_2, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_template_hierarchy(self): + """Test to verify template at same level in hierarchy""" + + # Validate the following + # 1. Verify template is visible for domain_1 + # 2. Verify template is also visible for domain_2 + + # Sleep to ensure that template state is reflected across + time.sleep(self.services["sleep"]) + + templates = list_templates( + self.apiclient, + templatefilter='self', + account=self.account_1.account.name, + domainid=self.domain_1.id + ) + self.assertEqual( + isinstance(templates, list), + True, + "Check List templates for a valid response" + ) + self.assertNotEqual( + len(templates), + 0, + "Check List Template response" + ) + + for template in templates: + self.assertEqual( + template.id, + self.template.id, + "Check Template ID for domain" + str(self.domain_1.name) + ) + + # Verify private service offering is not visible to other domain + templates = list_templates( + self.apiclient, + templatefilter='self', + account=self.account_2.account.name, + domainid=self.domain_2.id + ) + self.assertEqual( + isinstance(templates, list), + True, + "Check List templates for a valid response" + ) + self.assertNotEqual( + len(templates), + 0, + "Check List Service Offerings response" + ) + + for template in templates: + self.assertEqual( + template.id, + self.template.id, + "Check Template ID for domain" + str(self.domain_2.name) + ) + return + +class TestAddVmToSubDomain(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestAddVmToSubDomain, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Setup working Environment- Create domain, zone, pod cluster etc. + cls.domain = get_domain( + cls.api_client, + cls.services + ) + cls.zone = get_zone( + cls.api_client, + cls.services, + ) + cls.sub_domain = Domain.create( + cls.api_client, + cls.services["domain"], + parentdomainid=cls.domain.id + ) + + # Create account for doamin_1 + cls.account_1 = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + # Create an account for domain_2 + cls.account_2 = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.sub_domain.id + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.domain.id + ) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.vm_1 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=cls.template.id, + accountid=cls.account_1.account.name, + domainid=cls.account_1.account.domainid, + serviceofferingid=cls.service_offering.id + ) + + cls.vm_2 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=cls.template.id, + accountid=cls.account_2.account.name, + domainid=cls.account_2.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls._cleanup = [ + cls.account_2, + cls.account_1, + cls.sub_domain, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Clean up, terminate the created resources + cleanup_resources(cls.api_client,cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created resources + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + + def test_01_add_vm_to_subdomain(self): + """ Test Sub domain allowed to launch VM when a Domain level zone is + created""" + + # Validate the following + # 1. Verify VM created by Account_1 is in Running state + # 2. Verify VM created by Account_2 is in Running state + + vm_response = list_virtual_machines( + self.apiclient, + id=self.vm_1.id + ) + self.assertEqual( + isinstance(vm_response, list), + True, + "Check List VM for a valid response" + ) + self.assertNotEqual( + len(vm_response), + 0, + "Check List Template response" + ) + + for vm in vm_response: + self.debug("VM ID: %s and state: %s" % (vm.id, vm.state)) + self.assertEqual( + vm.state, + 'Running', + "Check State of Virtual machine" + ) + + vm_response = list_virtual_machines( + self.apiclient, + id=self.vm_2.id + ) + self.assertNotEqual( + len(vm_response), + 0, + "Check List Template response" + ) + + for vm in vm_response: + self.debug("VM ID: %s and state: %s" % (vm.id, vm.state)) + self.assertEqual( + vm.state, + 'Running', + "Check State of Virtual machine" + ) + return diff --git a/test/integration/component/test_blocker_bugs.py b/test/integration/component/test_blocker_bugs.py new file mode 100644 index 00000000000..4155a41aa13 --- /dev/null +++ b/test/integration/component/test_blocker_bugs.py @@ -0,0 +1,1273 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" Tests for Blocker bugs +""" +import marvin +from integration.lib.base import * +from integration.lib.utils import * +from integration.lib.common import * + +#Import Local Modules +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient + +class Services: + """Test Services + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "volume": { + "diskname": "APP Data Volume", + "size": 1, # in GBs + "diskdevice": "/dev/xvdb", # Data Disk + }, + "templates": { + "displaytext": 'Template from snapshot', + "name": 'Template from snapshot', + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + "templatefilter": 'self', + "url": "http://download.cloud.com/releases/2.0.0/UbuntuServer-10-04-64bit.vhd.bz2", + "hypervisor": 'XenServer', + "format" : 'VHD', + "isfeatured": True, + "ispublic": True, + "isextractable": True, + "passwordenabled":True, + }, + "paths": { + "mount_dir": "/mnt/tmp", + "sub_dir": "test", + "sub_lvl_dir1": "test1", + "sub_lvl_dir2": "test2", + "random_data": "random.data", + }, + "static_nat": { + "startport": 22, + "endport": 22, + "protocol": "TCP" + }, + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + # Cent OS 5.3 (64 bit) + "sleep":60, + "mode": 'advanced', + # Networking mode, Advanced, Basic + } + + +class TestSnapshots(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestSnapshots, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["volume"]["zoneid"] = cls.zone.id + + cls.services["template"] = cls.template.id + cls.services["zoneid"] = cls.zone.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=cls.template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_volume_from_snapshot(self): + """TS_BUG_001-Test Creating snapshot from volume having spaces in name(KVM) + """ + + # Validate the following + #1. Create a virtual machine and data volume + #2. Attach data volume to VM + #3. Login to machine; create temp/test directories on data volume + #4. Snapshot the Volume + #5. Create another Volume from snapshot + #6. Mount/Attach volume to another server + #7. Compare data + + random_data_0 = random_gen(100) + random_data_1 = random_gen(100) + + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.debug("Created volume with ID: %s" % volume.id) + self.virtual_machine.attach_volume( + self.apiclient, + volume + ) + self.debug("Attach volume: %s to VM: %s" % + (volume.id, self.virtual_machine.id)) + try: + ssh_client = self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH failed for VM: %s" % + self.virtual_machine.ipaddress) + + self.debug("Formatting volume: %s to ext3" % volume.id) + #Format partition using ext3 + format_volume_to_ext3( + ssh_client, + self.services["volume"]["diskdevice"] + ) + cmds = [ + "mkdir -p %s" % self.services["paths"]["mount_dir"], + "mount %s1 %s" % ( + self.services["volume"]["diskdevice"], + self.services["paths"]["mount_dir"] + ), + "mkdir -p %s/%s/{%s,%s} " % ( + self.services["paths"]["mount_dir"], + self.services["paths"]["sub_dir"], + self.services["paths"]["sub_lvl_dir1"], + self.services["paths"]["sub_lvl_dir2"] + ), + "echo %s > %s/%s/%s/%s" % ( + random_data_0, + self.services["paths"]["mount_dir"], + self.services["paths"]["sub_dir"], + self.services["paths"]["sub_lvl_dir1"], + self.services["paths"]["random_data"] + ), + "echo %s > %s/%s/%s/%s" % ( + random_data_1, + self.services["paths"]["mount_dir"], + self.services["paths"]["sub_dir"], + self.services["paths"]["sub_lvl_dir2"], + self.services["paths"]["random_data"] + ), + ] + for c in cmds: + self.debug("Command: %s" % c) + ssh_client.execute(c) + + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["paths"]["mount_dir"]), + ] + for c in cmds: + self.debug("Command: %s" % c) + ssh_client.execute(c) + + list_volume_response = Volume.list( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volume response for valid data" + ) + volume_response = list_volume_response[0] + #Create snapshot from attached volume + snapshot = Snapshot.create( + self.apiclient, + volume_response.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created snapshot: %s" % snapshot.id) + #Create volume from snapshot + volume_from_snapshot = Volume.create_from_snapshot( + self.apiclient, + snapshot.id, + self.services["volume"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created Volume: %s from Snapshot: %s" % ( + volume_from_snapshot.id, + snapshot.id)) + volumes = Volume.list( + self.apiclient, + id=volume_from_snapshot.id + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(volumes), + None, + "Check Volume list Length" + ) + self.assertEqual ( + volumes[0].id, + volume_from_snapshot.id, + "Check Volume in the List Volumes" + ) + #Attaching volume to new VM + new_virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + mode=self.services["mode"] + ) + self.debug("Deployed new VM for account: %s" % self.account.account.name) + self.cleanup.append(new_virtual_machine) + + self.debug("Attaching volume: %s to VM: %s" % ( + volume_from_snapshot.id, + new_virtual_machine.id + )) + + cmd = attachVolume.attachVolumeCmd() + cmd.id = volume_from_snapshot.id + cmd.virtualmachineid = new_virtual_machine.id + self.apiclient.attachVolume(cmd) + + try: + #Login to VM to verify test directories and files + ssh = new_virtual_machine.get_ssh_client() + + cmds = [ + "mkdir -p %s" % self.services["paths"]["mount_dir"], + "mount %s1 %s" % ( + self.services["volume"]["diskdevice"], + self.services["paths"]["mount_dir"] + ), + ] + + for c in cmds: + self.debug("Command: %s" % c) + ssh.execute(c) + + returned_data_0 = ssh.execute( + "cat %s/%s/%s/%s" % ( + self.services["paths"]["mount_dir"], + self.services["paths"]["sub_dir"], + self.services["paths"]["sub_lvl_dir1"], + self.services["paths"]["random_data"] + )) + returned_data_1 = ssh.execute( + "cat %s/%s/%s/%s" % ( + self.services["paths"]["mount_dir"], + self.services["paths"]["sub_dir"], + self.services["paths"]["sub_lvl_dir2"], + self.services["paths"]["random_data"] + )) + except Exception as e: + self.fail("SSH access failed for VM: %s" % + new_virtual_machine.ipaddress) + #Verify returned data + self.assertEqual( + random_data_0, + returned_data_0[0], + "Verify newly attached volume contents with existing one" + ) + self.assertEqual( + random_data_1, + returned_data_1[0], + "Verify newly attached volume contents with existing one" + ) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["paths"]["mount_dir"]), + ] + for c in cmds: + self.debug("Command: %s" % c) + ssh_client.execute(c) + return + + +class TestTemplate(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestTemplate, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["templates"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestTemplate, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_create_template(self): + """TS_BUG_002-Test to create and deploy VM using password enabled template + """ + + # Validate the following: + #1. Create a password enabled template + #2. Deploy VM using this template + #3. Deploy VM should return password set in template. + + self.debug("Registering a new template") + # Register new template + template = Template.register( + self.apiclient, + self.services["templates"], + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Registering template with ID: %s" % template.id) + try: + # Wait for template to download + template.download(self.apiclient) + except Exception as e: + self.fail("Exception while downloading template %s: %s"\ + % (template.id, e)) + + self.cleanup.append(template) + + # Wait for template status to be changed across + time.sleep(self.services["sleep"]) + + list_template_response = Template.list( + self.apiclient, + templatefilter=\ + self.services["templates"]["templatefilter"], + id=template.id, + zoneid=self.zone.id + ) + + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check list response returns a valid list" + ) + #Verify template response to check whether template added successfully + self.assertNotEqual( + len(list_template_response), + 0, + "Check template available in List Templates" + ) + template_response = list_template_response[0] + + self.assertEqual( + template_response.isready, + True, + "Check display text of newly created template" + ) + + # Deploy new virtual machine using template + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + ) + self.debug("Deployed VM with ID: %s " % virtual_machine.id) + self.assertEqual( + hasattr(virtual_machine,"password"), + True, + "Check if the deployed VM returned a password" + ) + return + + +class TestNATRules(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super(TestNATRules, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.public_ip = PublicIPAddress.create( + cls.api_client, + accountid=cls.account.account.name, + zoneid=cls.zone.id, + domainid=cls.account.account.domainid, + services=cls.services["virtual_machine"] + ) + cls._cleanup = [ + cls.virtual_machine, + cls.account, + cls.service_offering + ] + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestNATRules, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def tearDown(self): + cleanup_resources(self.apiclient, self.cleanup) + return + + def test_01_firewall_rules_port_fw(self): + """"Checking firewall rules deletion after static NAT disable""" + + # Validate the following: + #1. Enable static NAT for a VM + #2. Open up some ports. At this point there will be new rows in the + # firewall_rules table. + #3. Disable static NAT for the VM. + #4. Check fire wall rules are deleted from firewall_rules table. + + public_ip = self.public_ip.ipaddress + + # Enable Static NAT for VM + StaticNATRule.enable( + self.apiclient, + public_ip.id, + self.virtual_machine.id + ) + self.debug("Enabled static NAT for public IP ID: %s" % + public_ip.id) + #Create Static NAT rule + nat_rule = StaticNATRule.create( + self.apiclient, + self.services["static_nat"], + public_ip.id + ) + self.debug("Created Static NAT rule for public IP ID: %s" % + public_ip.id) + list_rules_repsonse = StaticNATRule.list( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(list_rules_repsonse, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_rules_repsonse), + 0, + "Check IP Forwarding Rule is created" + ) + self.assertEqual( + list_rules_repsonse[0].id, + nat_rule.id, + "Check Correct IP forwarding Rule is returned" + ) + # Verify the entries made in firewall_rules tables + self.debug( + "select id from user_ip_address where uuid = '%s';" \ + % public_ip.id + ) + qresultset = self.dbclient.execute( + "select id from user_ip_address where uuid = '%s';" \ + % public_ip.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check database query returns a valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + public_ip_id = qresult[0] + # Verify the entries made in firewall_rules tables + self.debug( + "select id, state from firewall_rules where ip_address_id = '%s';" \ + % public_ip_id + ) + qresultset = self.dbclient.execute( + "select id, state from firewall_rules where ip_address_id = '%s';" \ + % public_ip_id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check database query returns a valid data for firewall rules" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + for qresult in qresultset: + self.assertEqual( + qresult[1], + 'Active', + "Check state of the static NAT rule in database" + ) + + nat_rule.delete(self.apiclient) + + list_rules_repsonse = StaticNATRule.list( + self.apiclient, + id=nat_rule.id + ) + + self.assertEqual( + list_rules_repsonse, + None, + "Check Port Forwarding Rule is deleted" + ) + + # Verify the entries made in firewall_rules tables + self.debug( + "select id, state from firewall_rules where ip_address_id = '%s';" \ + % public_ip.id + ) + qresultset = self.dbclient.execute( + "select id, state from firewall_rules where ip_address_id = '%s';" \ + % public_ip.id + ) + + self.assertEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + return + + +class TestRouters(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestRouters, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + + # Create an account, domain etc + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"], + ) + cls.admin_account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.user_account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + cls._cleanup = [ + cls.service_offering, + cls.admin_account, + cls.user_account, + cls.domain + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, users etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_list_routers_admin(self): + """TS_BUG_007-Check listRouters() using Admin User + """ + + # Validate the following + # 1. PreReq: have rounters that are owned by other account + # 2. Create domain and create accounts in that domain + # 3. Create one VM for each account + # 4. Using Admin , run listRouters. It should return all the routers + + vm_1 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.admin_account.account.name, + domainid=self.admin_account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM with ID: %s" % vm_1.id) + vm_2 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.user_account.account.name, + domainid=self.user_account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM with ID: %s" % vm_2.id) + routers = list_routers( + self.apiclient, + account=self.admin_account.account.name, + domainid=self.admin_account.account.domainid, + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check list response returns a valid list" + ) + # ListRouter Should return 2 records + self.assertEqual( + len(routers), + 1, + "Check list router response" + ) + for router in routers: + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + return + + +class TestRouterRestart(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super(TestRouterRestart, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.vm_1 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.cleanup = [ + cls.vm_1, + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestRouterRestart, cls).getClsTestClient().getApiClient() + #Clean up, terminate the created templates + cleanup_resources(cls.api_client, cls.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + return + + + def test_01_restart_network_cleanup(self): + """TS_BUG_008-Test restart network + """ + + # Validate the following + # 1. When cleanup = true, router is destroyed and a new one created + # 2. New router will have new publicIp and linkLocalIp and + # all it's services should resume + + # Find router associated with user account + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + #Store old values before restart + old_linklocalip = router.linklocalip + + timeout = 10 + # Network should be in Implemented or Setup stage before restart + while True: + networks = Network.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + network = networks[0] + if network.state in ["Implemented", "Setup"]: + break + elif timeout == 0: + break + else: + time.sleep(60) + timeout = timeout - 1 + + self.debug("Restarting network: %s" % network.id) + cmd = restartNetwork.restartNetworkCmd() + cmd.id = network.id + cmd.cleanup = True + self.apiclient.restartNetwork(cmd) + + # Get router details after restart + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + self.assertNotEqual( + router.linklocalip, + old_linklocalip, + "Check link-local IP after restart" + ) + return + + +class TestTemplates(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.services = Services().services + cls.api_client = super(TestTemplates, cls).getClsTestClient().getApiClient() + + # Get Zone, templates etc + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + # create virtual machine + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + ) + #Stop virtual machine + cls.virtual_machine.stop(cls.api_client) + + #Wait before server has be successfully stopped + time.sleep(30) + list_volume = Volume.list( + cls.api_client, + virtualmachineid=cls.virtual_machine.id, + type='ROOT', + listall=True + ) + try: + if isinstance(list_volume, list): + cls.volume = list_volume[0] + except Exception as e: + raise Exception("Warning: Exception during setup : %s" % e) + + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestTemplates, cls).getClsTestClient().getApiClient() + #Cleanup created resources such as templates and VMs + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_check_template_size(self): + """TS_BUG_009-Test the size of template created from root disk + """ + + # Validate the following: + # 1. Deploy new VM using the template created from Volume + # 2. VM should be in Up and Running state + + #Create template from volume + template = Template.create( + self.apiclient, + self.services["templates"], + self.volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Creating template with ID: %s" % template.id) + # Volume and Template Size should be same + self.assertEqual( + template.size, + self.volume.size, + "Check if size of template and volume are same" + ) + return + + def test_02_check_size_snapshotTemplate(self): + """TS_BUG_010-Test check size of snapshot and template + """ + + # Validate the following + # 1. Deploy VM using default template, small service offering + # and small data disk offering. + # 2. Perform snapshot on the root disk of this VM. + # 3. Create a template from snapshot. + # 4. Check the size of snapshot and template + + # Create a snapshot from the ROOTDISK + snapshot = Snapshot.create( + self.apiclient, + self.volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created snapshot with ID: %s" % snapshot.id) + snapshots = Snapshot.list( + self.apiclient, + id=snapshot.id + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list snapshots call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check snapshot id in list resources call" + ) + + # Generate template from the snapshot + template = Template.create_from_snapshot( + self.apiclient, + snapshot, + self.services["templates"] + ) + self.cleanup.append(template) + + self.debug("Created template from snapshot with ID: %s" % template.id) + templates = Template.list( + self.apiclient, + templatefilter=\ + self.services["templates"]["templatefilter"], + id=template.id + ) + self.assertEqual( + isinstance(templates, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + templates, + None, + "Check if result exists in list item call" + ) + + self.assertEqual( + templates[0].isready, + True, + "Check new template state in list templates call" + ) + # check size of template with that of snapshot + self.assertEqual( + templates[0].size, + self.volume.size, + "Check if size of snapshot and template matches" + ) + return + + def test_03_resuse_template_name(self): + """TS_BUG_011-Test Reusing deleted template name + """ + + # Validate the following + # 1. Deploy VM using default template, small service offering + # and small data disk offering. + # 2. Perform snapshot on the root disk of this VM. + # 3. Create a template from snapshot. + # 4. Delete the template and create a new template with same name + # 5. Template should be created succesfully + + # Create a snapshot from the ROOTDISK + snapshot = Snapshot.create( + self.apiclient, + self.volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created snapshot with ID: %s" % snapshot.id) + snapshots = Snapshot.list( + self.apiclient, + id=snapshot.id + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list snapshots call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check snapshot id in list resources call" + ) + + # Generate template from the snapshot + template = Template.create_from_snapshot( + self.apiclient, + snapshot, + self.services["templates"], + random_name=False + ) + self.debug("Created template from snapshot: %s" % template.id) + templates = Template.list( + self.apiclient, + templatefilter=\ + self.services["templates"]["templatefilter"], + id=template.id + ) + self.assertEqual( + isinstance(templates, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + templates, + None, + "Check if result exists in list item call" + ) + + self.assertEqual( + templates[0].isready, + True, + "Check new template state in list templates call" + ) + + self.debug("Deleting template: %s" % template.id) + template.delete(self.apiclient) + + # Wait for some time to ensure template state is reflected in other calls + time.sleep(self.services["sleep"]) + + # Generate template from the snapshot + self.debug("Creating template from snapshot: %s with same name" % + template.id) + template = Template.create_from_snapshot( + self.apiclient, + snapshot, + self.services["templates"], + random_name=False + ) + + templates = Template.list( + self.apiclient, + templatefilter=\ + self.services["templates"]["templatefilter"], + id=template.id + ) + self.assertEqual( + isinstance(templates, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + templates, + None, + "Check if result exists in list item call" + ) + + self.assertEqual( + templates[0].name, + self.services["templates"]["name"], + "Check the name of the template" + ) + return diff --git a/test/integration/component/test_egress_rules.py b/test/integration/component/test_egress_rules.py new file mode 100644 index 00000000000..d0e00498ac6 --- /dev/null +++ b/test/integration/component/test_egress_rules.py @@ -0,0 +1,2336 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 + +""" P1 for Egresss & Ingress rules +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + +#Import System modules +import time +import subprocess + + +class Services: + """Test Security groups Services + """ + + def __init__(self): + self.services = { + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "fr3sca", + }, + "virtual_machine": { + # Create a small virtual machine instance with disk offering + "displayname": "Test VM", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + "userdata": 'This is sample data', + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "security_group": { + "name": 'SSH', + "protocol": 'TCP', + "startport": 22, + "endport": 22, + "cidrlist": '0.0.0.0/0', + }, + "sg_invalid_port": { + "name": 'SSH', + "protocol": 'TCP', + "startport": -22, + "endport": -22, + "cidrlist": '0.0.0.0/0', + }, + "sg_invalid_cidr": { + "name": 'SSH', + "protocol": 'TCP', + "startport": 22, + "endport": 22, + "cidrlist": '0.0.0.10' + }, + "sg_cidr_anywhere": { + "name": 'SSH', + "protocol": 'TCP', + "startport": 22, + "endport": 22, + "cidrlist": '0.0.0.0/0' + }, + "sg_cidr_restricted": { + "name": 'SSH', + "protocol": 'TCP', + "startport": 22, + "endport": 22, + "cidrlist": '10.0.0.1/24', + }, + "sg_account": { + "name": 'SSH', + "protocol": 'TCP', + "startport": 22, + "endport": 22, + "cidrlist": '0.0.0.0/0', + }, + "mgmt_server": { + "username": "root", + "password": "fr3sca", + "ipaddress": "192.168.100.21" + }, + "ostypeid": '85cb528f-72ed-4df9-ac6a-f6ccf0892ff2', + # CentOS 5.3 (64-bit) + "sleep": 60, + "timeout": 10, + "mode": 'basic', + # Networking mode: Basic or Advanced + } + + +class TestDefaultSecurityGroupEgress(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestDefaultSecurityGroupEgress, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_deployVM_InDefaultSecurityGroup(self): + """Test deploy VM in default security group with no egress rules + """ + + # Validate the following: + # 1. Deploy a VM. + # 2. Deployed VM should be running, verify with listVirtualMachiens + # 3. listSecurityGroups for this account. should list the default + # security group with no egress rules + # 4. listVirtualMachines should show that the VM belongs to default + # security group + + self.debug("Deploying VM in account: %s" % self.account.account.name) + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM with ID: %s" % self.virtual_machine.id) + self.cleanup.append(self.virtual_machine) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check for list VM response" + ) + vm_response = list_vm_response[0] + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + + self.assertEqual( + + vm_response.id, + self.virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + + self.assertEqual( + vm_response.state, + 'Running', + "VM state should be running" + ) + self.assertEqual( + hasattr(vm_response, "securitygroup"), + True, + "List VM response should have atleast one security group" + ) + + # Verify listSecurity groups response + security_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(security_groups, list), + True, + "Check for list security groups response" + ) + self.assertEqual( + len(security_groups), + 1, + "Check List Security groups response" + ) + self.debug("List Security groups response: %s" % + str(security_groups)) + sec_grp = security_groups[0] + self.assertEqual( + sec_grp.name, + 'default', + "List Sec Group should only list default sec. group" + ) + return + + +class TestAuthorizeIngressRule(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestAuthorizeIngressRule, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_authorizeIngressRule(self): + """Test authorize ingress rule + """ + + # Validate the following: + # 1. createaccount of type user + # 2. createsecuritygroup (ssh) for this account + # 3. authorizeSecurityGroupIngress to allow ssh access to the VM + # 4. deployVirtualMachine into this security group (ssh). deployed VM + # should be Running + # 5. listSecurityGroups should show two groups, default and ssh + # 6. verify that ssh-access into the VM is now allowed + # 7. verify from within the VM is able to ping outside world + # (ping www.google.com) + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + self.debug("Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.ssh_ip) + ssh = self.virtual_machine.get_ssh_client() + + # Ping to outsite world + res = ssh.execute("ping -c 1 www.google.com") + # res = 64 bytes from maa03s17-in-f20.1e100.net (74.125.236.212): + # icmp_req=1 ttl=57 time=25.9 ms + # --- www.l.google.com ping statistics --- + # 1 packets transmitted, 1 received, 0% packet loss, time 0ms + # rtt min/avg/max/mdev = 25.970/25.970/25.970/0.000 ms + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + result = str(res) + self.assertEqual( + result.count("1 received"), + 1, + "Ping to outside world from VM should be successful" + ) + return + + +class TestDefaultGroupEgress(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestDefaultGroupEgress, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_default_group_with_egress(self): + """Test default group with egress rule before VM deploy and ping, ssh + """ + + # Validate the following: + # 1. createaccount of type user + # 2. createsecuritygroup (ssh) for this account + # 3. authorizeSecurityGroupIngress to allow ssh access to the VM + # 4. authorizeSecurityGroupEgress to allow ssh access only out to + # CIDR: 0.0.0.0/0 + # 5. deployVirtualMachine into this security group (ssh) + # 6. deployed VM should be Running, ssh should be allowed into the VM, + # ping out to google.com from the VM should fail, + # ssh from within VM to mgt server should pass + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + self.debug("Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + + # Authorize Security group to SSH to VM + self.debug("Authorizing egress rule for sec group ID: %s for ssh access" + % security_group.id) + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(egress_rule, dict), + True, + "Check egress rule created properly" + ) + ssh_egress_rule = (egress_rule["egressrule"][0]).__dict__ + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.ssh_ip) + + ssh = self.virtual_machine.get_ssh_client() + + self.debug("Ping to google.com from VM") + # Ping to outsite world + res = ssh.execute("ping -c 1 www.google.com") + # res = 64 bytes from maa03s17-in-f20.1e100.net (74.125.236.212): + # icmp_req=1 ttl=57 time=25.9 ms + # --- www.l.google.com ping statistics --- + # 1 packets transmitted, 1 received, 0% packet loss, time 0ms + # rtt min/avg/max/mdev = 25.970/25.970/25.970/0.000 ms + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + result = str(res) + self.assertEqual( + result.count("0 received"), + 1, + "Ping to outside world from VM should be successful" + ) + + try: + self.debug("SSHing into management server from VM") + res = ssh.execute("ssh %s@%s" % ( + self.services["mgmt_server"]["username"], + self.services["mgmt_server"]["ipaddress"] + )) + self.debug("SSH result: %s" % str(res)) + + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + result = str(res) + self.assertNotEqual( + result.count("No route to host"), + 1, + "SSH into management server from VM should be successful" + ) + return + + +class TestDefaultGroupEgressAfterDeploy(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestDefaultGroupEgressAfterDeploy, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_default_group_with_egress(self): + """ Test default group with egress rule added after vm deploy and ping, + ssh test + """ + + # Validate the following: + # 1. createaccount of type user + # 2. createsecuritygroup (ssh) for this account + # 3. authorizeSecurityGroupIngress to allow ssh access to the VM + # 4. deployVirtualMachine into this security group (ssh) + # 5. authorizeSecurityGroupEgress to allow ssh access only out to + # CIDR: 0.0.0.0/0 + # 6. deployed VM should be Running, ssh should be allowed into the VM, + # ping out to google.com from the VM should fail + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + self.debug("Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s for ssh access" + % security_group.id) + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(egress_rule, dict), + True, + "Check egress rule created properly" + ) + ssh_egress_rule = (egress_rule["egressrule"][0]).__dict__ + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.ssh_ip) + ssh = self.virtual_machine.get_ssh_client() + + self.debug("Ping to google.com from VM") + # Ping to outsite world + res = ssh.execute("ping -c 1 www.google.com") + # res = 64 bytes from maa03s17-in-f20.1e100.net (74.125.236.212): + # icmp_req=1 ttl=57 time=25.9 ms + # --- www.l.google.com ping statistics --- + # 1 packets transmitted, 1 received, 0% packet loss, time 0ms + # rtt min/avg/max/mdev = 25.970/25.970/25.970/0.000 ms + self.debug("SSH result: %s" %str(res)) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + result = str(res) + self.assertEqual( + result.count("0 received"), + 1, + "Ping to outside world from VM should be successful" + ) + return + + +class TestRevokeEgressRule(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestRevokeEgressRule, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_revoke_egress_rule(self): + """Test revoke security group egress rule + """ + + # Validate the following: + # 1. createaccount of type user + # 2. createsecuritygroup (ssh) for this account + # 3. authorizeSecurityGroupIngress to allow ssh access to the VM + # 4. authorizeSecurityGroupEgress to allow ssh access only out to + # CIDR: 0.0.0.0/0 + # 5. deployVirtualMachine into this security group (ssh) + # 6. deployed VM should be Running, ssh should be allowed into the VM, + # ping out to google.com from the VM should fail, + # ssh from within VM to mgt server should pass + # 7. Revoke egress rule. Verify ping and SSH access to management server + # is restored + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + self.debug( + "Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s for ssh access" + % security_group.id) + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(egress_rule, dict), + True, + "Check egress rule created properly" + ) + ssh_egress_rule = (egress_rule["egressrule"][0]).__dict__ + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.ssh_ip) + ssh = self.virtual_machine.get_ssh_client() + + self.debug("Ping to google.com from VM") + # Ping to outsite world + res = ssh.execute("ping -c 1 www.google.com") + # res = 64 bytes from maa03s17-in-f20.1e100.net (74.125.236.212): + # icmp_req=1 ttl=57 time=25.9 ms + # --- www.l.google.com ping statistics --- + # 1 packets transmitted, 1 received, 0% packet loss, time 0ms + # rtt min/avg/max/mdev = 25.970/25.970/25.970/0.000 ms + self.debug("SSH result: %s" % str(res)) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + result = str(res) + self.assertEqual( + result.count("0 received"), + 1, + "Ping to outside world from VM should be successful" + ) + + try: + self.debug("SSHing into management server from VM") + res = ssh.execute("ssh %s@%s" % ( + self.services["mgmt_server"]["username"], + self.services["mgmt_server"]["ipaddress"] + )) + self.debug("SSH result: %s" % str(res)) + + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + result = str(res) + self.assertNotEqual( + result.count("No route to host"), + 1, + "SSH into management server from VM should be successful" + ) + + self.debug( + "Revoke Egress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + + result = security_group.revokeEgress( + self.apiclient, + id=ssh_egress_rule["ruleid"] + ) + self.debug("Revoke egress rule result: %s" % result) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.ssh_ip) + ssh = self.virtual_machine.get_ssh_client(reconnect=True) + + self.debug("Ping to google.com from VM") + # Ping to outsite world + res = ssh.execute("ping -c 1 www.google.com") + # res = 64 bytes from maa03s17-in-f20.1e100.net (74.125.236.212): + # icmp_req=1 ttl=57 time=25.9 ms + # --- www.l.google.com ping statistics --- + # 1 packets transmitted, 1 received, 0% packet loss, time 0ms + # rtt min/avg/max/mdev = 25.970/25.970/25.970/0.000 ms + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + result = str(res) + self.assertEqual( + result.count("1 received"), + 1, + "Ping to outside world from VM should be successful" + ) + + try: + self.debug("SSHing into management server from VM") + res = ssh.execute("ssh %s@%s" % ( + self.services["mgmt_server"]["username"], + self.services["mgmt_server"]["ipaddress"] + )) + self.debug("SSH result: %s" % str(res)) + + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + result = str(res) + self.assertNotEqual( + result.count("No route to host"), + 1, + "SSH into management server from VM should be successful" + ) + return + + +class TestInvalidAccountAuthroize(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestInvalidAccountAuthroize, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_invalid_account_authroize(self): + """Test invalid account authroize + """ + + # Validate the following: + # 1. createaccount of type user + # 2. createsecuritygroup (ssh) for this account + # 3. authorizeSecurityGroupEgress to allow ssh access only out to + # non-existent random account and default security group + # 4. listSecurityGroups should show ssh and default security groups + # 5. authorizeSecurityGroupEgress API should fail since there is no + # account + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s for ssh access" + % security_group.id) + with self.assertRaises(Exception): + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["security_group"], + account=random_gen(), + domainid=self.account.account.domainid + ) + return + + +class TestMultipleAccountsEgressRuleNeg(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestMultipleAccountsEgressRuleNeg, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.accountA = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.accountB = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.accountA.account.name + cls._cleanup = [ + cls.accountA, + cls.accountB, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_multiple_account_egress_rule_negative(self): + """Test multiple account egress rules negative case + """ + + # Validate the following: + # 1. createaccount of type user A + # 2. createaccount of type user B + # 3. createsecuritygroup (SSH-A) for account A + # 4. authorizeSecurityGroupEgress in account A to allow ssh access + # only out to VMs in account B's default security group + # 5. authorizeSecurityGroupIngress in account A to allow ssh incoming + # access from anywhere into Vm's of account A. listSecurityGroups + # for account A should show two groups (default and ssh-a) and ssh + # ingress rule and account based egress rule + # 6. deployVM in account A into security group SSH-A. deployed VM + # should be Running + # 7. deployVM in account B. deployed VM should be Running + # 8. ssh into VM in account A and from there ssh to VM in account B. + # ssh should fail + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.accountA.account.name, + domainid=self.accountA.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.accountA.account.name, + domainid=self.accountA.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s for ssh access" + % security_group.id) + # Authorize to only account not CIDR + user_secgrp_list = {self.accountB.account.name: 'default'} + + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["sg_account"], + account=self.accountA.account.name, + domainid=self.accountA.account.domainid, + user_secgrp_list=user_secgrp_list + ) + + self.assertEqual( + isinstance(egress_rule, dict), + True, + "Check egress rule created properly" + ) + ssh_egress_rule = (egress_rule["egressrule"][0]).__dict__ + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.accountA.account.name, + domainid=self.accountA.account.domainid + ) + + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + + self.virtual_machineA = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.accountA.account.name, + domainid=self.accountA.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.cleanup.append(self.virtual_machineA) + self.debug("Deploying VM in account: %s" % self.accountA.account.name) + vms = VirtualMachine.list( + self.apiclient, + id=self.virtual_machineA.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VM should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state after deployment should be running" + ) + self.debug("VM: %s state: %s" % (vm.id, vm.state)) + + self.virtual_machineB = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.accountB.account.name, + domainid=self.accountB.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(self.virtual_machineB) + self.debug("Deploying VM in account: %s" % self.accountB.account.name) + + vms = VirtualMachine.list( + self.apiclient, + id=self.virtual_machineB.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VM should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state after deployment should be running" + ) + self.debug("VM: %s state: %s" % (vm.id, vm.state)) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machineA.ssh_ip) + ssh = self.virtual_machineA.get_ssh_client() + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machineA.ipaddress, e) + ) + + try: + self.debug("SSHing into VM type B from VM A") + self.debug("VM IP: %s" % self.virtual_machineB.ssh_ip) + res = ssh.execute("ssh %s@%s" % ( + self.services["virtual_machine"]["username"], + self.virtual_machineB.ssh_ip + )) + self.debug("SSH result: %s" % str(res)) + + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machineA.ipaddress, e) + ) + result = str(res) + self.assertEqual( + result.count("Connection timed out"), + 1, + "SSH into management server from VM should not be successful" + ) + return + + +class TestMultipleAccountsEgressRule(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestMultipleAccountsEgressRule, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.accountA = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.accountB = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.accountA.account.name + cls._cleanup = [ + cls.accountA, + cls.accountB, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_multiple_account_egress_rule_positive(self): + """Test multiple account egress rules positive case + """ + + # Validate the following: + # 1. createaccount of type user A + # 2. createaccount of type user B + # 3. createsecuritygroup (SSH-A) for account A + # 4. authorizeSecurityGroupEgress in account A to allow ssh access + # only out to VMs in account B's default security group + # 5. authorizeSecurityGroupIngress in account A to allow ssh incoming + # access from anywhere into Vm's of account A. listSecurityGroups + # for account A should show two groups (default and ssh-a) and ssh + # ingress rule and account based egress rule + # 6. deployVM in account A into security group SSH-A. deployed VM + # should be Running + # 7. deployVM in account B. deployed VM should be Running + # 8. ssh into VM in account A and from there ssh to VM in account B. + # ssh should fail + + security_groupA = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.accountA.account.name, + domainid=self.accountA.account.domainid + ) + self.debug("Created security group with ID: %s" % security_groupA.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.accountA.account.name, + domainid=self.accountA.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + + security_groupB = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.accountB.account.name, + domainid=self.accountB.account.domainid + ) + self.debug("Created security group with ID: %s" % security_groupB.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.accountB.account.name, + domainid=self.accountB.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s for ssh access" + % security_groupA.id) + # Authorize to only account not CIDR + user_secgrp_list = {self.accountB.account.name: security_groupB.name} + + egress_rule = security_groupA.authorizeEgress( + self.apiclient, + self.services["sg_account"], + account=self.accountA.account.name, + domainid=self.accountA.account.domainid, + user_secgrp_list=user_secgrp_list + ) + + self.assertEqual( + isinstance(egress_rule, dict), + True, + "Check egress rule created properly" + ) + ssh_egress_rule = (egress_rule["egressrule"][0]).__dict__ + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing ingress rule for sec group ID: %s for ssh access" + % security_groupA.id) + ingress_ruleA = security_groupA.authorize( + self.apiclient, + self.services["security_group"], + account=self.accountA.account.name, + domainid=self.accountA.account.domainid + ) + + self.assertEqual( + isinstance(ingress_ruleA, dict), + True, + "Check ingress rule created properly" + ) + + ssh_ruleA = (ingress_ruleA["ingressrule"][0]).__dict__ + + self.virtual_machineA = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.accountA.account.name, + domainid=self.accountA.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_groupA.id] + ) + self.cleanup.append(self.virtual_machineA) + self.debug("Deploying VM in account: %s" % self.accountA.account.name) + + vms = VirtualMachine.list( + self.apiclient, + id=self.virtual_machineA.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VM should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state after deployment should be running" + ) + self.debug("VM: %s state: %s" % (vm.id, vm.state)) + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing ingress rule for sec group ID: %s for ssh access" + % security_groupB.id) + ingress_ruleB = security_groupB.authorize( + self.apiclient, + self.services["security_group"], + account=self.accountB.account.name, + domainid=self.accountB.account.domainid + ) + + self.assertEqual( + isinstance(ingress_ruleB, dict), + True, + "Check ingress rule created properly" + ) + + ssh_ruleB = (ingress_ruleB["ingressrule"][0]).__dict__ + + self.virtual_machineB = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.accountB.account.name, + domainid=self.accountB.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_groupB.id] + ) + self.cleanup.append(self.virtual_machineB) + self.debug("Deploying VM in account: %s" % self.accountB.account.name) + + vms = VirtualMachine.list( + self.apiclient, + id=self.virtual_machineB.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VM should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state after deployment should be running" + ) + self.debug("VM: %s state: %s" % (vm.id, vm.state)) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machineA.ssh_ip) + ssh = self.virtual_machineA.get_ssh_client() + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machineA.ipaddress, e) + ) + + try: + self.debug("SSHing into VB type B from VM A") + self.debug("VM IP: %s" % self.virtual_machineB.ssh_ip) + + res = ssh.execute("ssh %s@%s" % ( + self.services["virtual_machine"]["username"], + self.virtual_machineB.ssh_ip + )) + self.debug("SSH result: %s" % str(res)) + + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machineA.ipaddress, e) + ) + result = str(res) + self.assertNotEqual( + result.count("Connection timed out"), + 1, + "SSH into management server from VM should be successful" + ) + return + + +class TestStartStopVMWithEgressRule(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestStartStopVMWithEgressRule, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_start_stop_vm_egress(self): + """ Test stop start Vm with egress rules + """ + + # Validate the following: + # 1. createaccount of type user + # 2. createsecuritygroup (ssh) for this account + # 3. authorizeSecurityGroupIngress to allow ssh access to the VM + # 4. authorizeSecurityGroupEgress to allow ssh access only out to + # CIDR: 0.0.0.0/0 + # 5. deployVirtualMachine into this security group (ssh) + # 6. stopVirtualMachine + # 7. startVirtualMachine + # 8. ssh in to VM + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + self.debug( + "Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s for ssh access" + % security_group.id) + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(egress_rule, dict), + True, + "Check egress rule created properly" + ) + ssh_egress_rule = (egress_rule["egressrule"][0]).__dict__ + + # Stop virtual machine + self.debug("Stopping virtual machine: %s" % self.virtual_machine.id) + self.virtual_machine.stop(self.apiclient) + + vms = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VM should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Stopped", + "VM state should be stopped" + ) + self.debug("VM: %s state: %s" % (vm.id, vm.state)) + + # Start virtual machine + self.debug("Starting virtual machine: %s" % self.virtual_machine.id) + self.virtual_machine.start(self.apiclient) + + vms = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VM should return a valid list" + ) + vm = vms[0] + self.assertEqual( + vm.state, + "Running", + "VM state should be stopped" + ) + self.debug("VM: %s state: %s" % (vm.id, vm.state)) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.ssh_ip) + ssh = self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + return + + +@unittest.skip("Valid bug- ID: CS-12647") +class TestInvalidParametersForEgress(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestInvalidParametersForEgress, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_invalid_parameters(self): + """ Test invalid parameters for egress rules + """ + + # Validate the following: + # 1. createUserAccount + # 2. createSecurityGroup (test) + # 3. authorizeEgressRule (negative port) - Should fail + # 4. authorizeEgressRule (invalid CIDR) - Should fail + # 5. authorizeEgressRule (invalid account) - Should fail + # 6. authorizeEgressRule (22, cidr: anywhere) and + # authorizeEgressRule (22, cidr: restricted) - Should pass + # 7. authorizeEgressRule (21, cidr : 10.1.1.0/24) and + # authorizeEgressRule (21, cidr: 10.1.1.0/24) - Should fail + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s with invalid port" + % security_group.id) + with self.assertRaises(Exception): + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["sg_invalid_port"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug( + "Authorizing egress rule for sec group ID: %s with invalid cidr" + % security_group.id) + with self.assertRaises(Exception): + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["sg_invalid_cidr"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug( + "Authorizing egress rule for sec group ID: %s with invalid account" + % security_group.id) + with self.assertRaises(Exception): + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["security_group"], + account=random_gen(), + domainid=self.account.account.domainid + ) + self.debug( + "Authorizing egress rule for sec group ID: %s with cidr: anywhere and port: 22" + % security_group.id) + egress_rule_A = security_group.authorizeEgress( + self.apiclient, + self.services["sg_cidr_anywhere"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(egress_rule_A, dict), + True, + "Check egress rule created properly" + ) + + egress_rule_R = security_group.authorizeEgress( + self.apiclient, + self.services["sg_cidr_restricted"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(egress_rule_R, dict), + True, + "Check egress rule created properly" + ) + + self.debug( + "Authorizing egress rule for sec group ID: %s with duplicate port" + % security_group.id) + with self.assertRaises(Exception): + security_group.authorizeEgress( + self.apiclient, + self.services["sg_cidr_restricted"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + return + + +class TestEgressAfterHostMaintainance(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestEgressAfterHostMaintainance, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.pod = get_pod( + cls.api_client, + zoneid=cls.zone.id + ) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_egress_after_host_maintainance(self): + """Test maintenance case for egress + """ + + # Validate the following: + # 1. createaccount of type user + # 2. createsecuritygroup (ssh) for this account + # 3. authorizeSecurityGroupIngress to allow ssh access to the VM + # 4. authorizeSecurityGroupEgress to allow ssh access only out to + # CIDR: 0.0.0.0/0 + # 5. deployVirtualMachine into this security group (ssh) + # 6. deployed VM should be Running, ssh should be allowed into the VM + # 7. Enable maintainance mode for host, cance maintainance mode + # 8. User should be able to SSH into VM after maintainace + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + self.debug( + "Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + + # Authorize Security group to SSH to VM + self.debug( + "Authorizing egress rule for sec group ID: %s for ssh access" + % security_group.id) + egress_rule = security_group.authorizeEgress( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(egress_rule, dict), + True, + "Check egress rule created properly" + ) + ssh_egress_rule = (egress_rule["egressrule"][0]).__dict__ + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.id) + ssh = self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + vms = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "Check list VMs response for valid host" + ) + vm = vms[0] + + self.debug("Enabling host maintainance for ID: %s" % vm.hostid) + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = vm.hostid + self.apiclient.prepareHostForMaintenance(cmd) + + self.debug("Canceling host maintainance for ID: %s" % vm.hostid) + cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() + cmd.id = vm.hostid + self.apiclient.cancelHostMaintenance(cmd) + + self.debug("Waiting for SSVMs to come up") + wait_for_ssvms( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id, + ) + self.debug("Starting VM: %s" % self.virtual_machine.id) + + self.virtual_machine.start(self.apiclient) + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.id) + ssh = self.virtual_machine.get_ssh_client(reconnect=True) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + return diff --git a/test/integration/component/test_high_availability.py b/test/integration/component/test_high_availability.py new file mode 100644 index 00000000000..b4ccbafc41a --- /dev/null +++ b/test/integration/component/test_high_availability.py @@ -0,0 +1,969 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 + +""" P1 tests for high availability +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +import datetime + + +class Services: + """Test network offering Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "HA", + "lastname": "HA", + "username": "HA", + # Random characters are appended for unique + # username + "password": "password", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "lbrule": { + "name": "SSH", + "alg": "roundrobin", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "protocol": "TCP" + }, + "fw_rule":{ + "startport": 1, + "endport": 6000, + "cidr": '55.55.0.0/11', + # Any network (For creating FW rule) + }, + "virtual_machine": { + "displayname": "VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostypeid": '9958b10f-9e5d-4ef1-908d-a047372d823b', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 100, + "mode":'advanced' + } + + +class TestHighAvailability(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super( + TestHighAvailability, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain( + cls.api_client, + cls.services + ) + cls.zone = get_zone( + cls.api_client, + cls.services + ) + cls.pod = get_pod( + cls.api_client, + zoneid=cls.zone.id, + services=cls.services + ) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + offerha=True + ) + cls._cleanup = [ + cls.service_offering, + ] + return + +# @classmethod +# def tearDownClass(cls): +# try: +# #Cleanup resources used +# cleanup_resources(cls.api_client, cls._cleanup) +# except Exception as e: +# raise Exception("Warning: Exception during cleanup : %s" % e) +# return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup = [self.account] + return + +# def tearDown(self): +# try: +# #Clean up, terminate the created accounts, domains etc +# cleanup_resources(self.apiclient, self.cleanup) +# self.testClient.close() +# except Exception as e: +# raise Exception("Warning: Exception during cleanup : %s" % e) +# return + + def test_01_host_maintenance_mode(self): + """Test host maintenance mode + """ + + # Validate the following + # 1. Create Vms. Acquire IP. Create port forwarding & load balancing + # rules for Vms. + # 2. Host 1: put to maintenance mode. All Vms should failover to Host + # 2 in cluster. Vms should be in running state. All port forwarding + # rules and load balancing Rules should work. + # 3. After failover to Host 2 succeeds, deploy Vms. Deploy Vms on host + # 2 should succeed. + # 4. Host 1: cancel maintenance mode. + # 5. Host 2 : put to maintenance mode. All Vms should failover to + # Host 1 in cluster. + # 6. After failover to Host 1 succeeds, deploy VMs. Deploy Vms on + # host 1 should succeed. + + hosts = Host.list( + self.apiclient, + zoneid=self.zone.id, + resourcestate='Enabled', + type='Routing' + ) + self.assertEqual( + isinstance(hosts, list), + True, + "List hosts should return valid host response" + ) + self.assertEqual( + len(hosts), + 2, + "There must be two hosts present in a cluster" + ) + self.debug("Checking HA with hosts: %s, %s" % ( + hosts[0].name, + hosts[1].name + )) + self.debug("Deploying VM in account: %s" % self.account.account.name) + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + self.debug("Deployed VM on host: %s" % vm.hostid) + self.assertEqual( + vm.state, + "Running", + "Deployed VM should be in RUnning state" + ) + networks = Network.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return valid list for the account" + ) + network = networks[0] + + self.debug("Associating public IP for account: %s" % + self.account.account.name) + public_ip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=network.id + ) + + self.debug("Associated %s with network %s" % ( + public_ip.ipaddress.ipaddress, + network.id + )) + self.debug("Creating PF rule for IP address: %s" % + public_ip.ipaddress.ipaddress) + nat_rule= NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=public_ip.ipaddress.id + ) + + self.debug("Creating LB rule on IP with NAT: %s" % + public_ip.ipaddress.ipaddress) + + # Create Load Balancer rule on IP already having NAT rule + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=public_ip.ipaddress.id, + accountid=self.account.account.name + ) + self.debug("Created LB rule with ID: %s" % lb_rule.id) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % virtual_machine.id) + ssh = virtual_machine.get_ssh_client( + ipaddress=public_ip.ipaddress.ipaddress) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (virtual_machine.ipaddress, e) + ) + + first_host = vm.hostid + self.debug("Enabling maintenance mode for host %s" % vm.hostid) + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = first_host + self.apiclient.prepareHostForMaintenance(cmd) + + self.debug("Waiting for SSVMs to come up") + wait_for_ssvms( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id, + ) + + timeout = self.services["timeout"] + # Poll and check state of VM while it migrates from one host to another + while True: + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + + self.debug("VM 1 state: %s" % vm.state) + if vm.state in ["Stopping", "Stopped", "Running", "Starting"]: + if vm.state == "Running": + break + else: + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + else: + self.fail( + "VM migration from one-host-to-other failed while enabling maintenance" + ) + second_host = vm.hostid + self.assertEqual( + vm.state, + "Running", + "VM should be in Running state after enabling host maintenance" + ) + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % virtual_machine.id) + ssh = virtual_machine.get_ssh_client( + ipaddress=public_ip.ipaddress.ipaddress) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (virtual_machine.ipaddress, e) + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + # Spawn an instance on other host + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_2.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + self.debug("Deployed VM on host: %s" % vm.hostid) + self.debug("VM 2 state: %s" % vm.state) + self.assertEqual( + vm.state, + "Running", + "Deployed VM should be in Running state" + ) + + self.debug("Canceling host maintenance for ID: %s" % first_host) + cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() + cmd.id = first_host + self.apiclient.cancelHostMaintenance(cmd) + self.debug("Maintenance mode canceled for host: %s" % first_host) + + self.debug("Enabling maintenance mode for host %s" % second_host) + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = second_host + self.apiclient.prepareHostForMaintenance(cmd) + self.debug("Maintenance mode enabled for host: %s" % second_host) + + self.debug("Waiting for SSVMs to come up") + wait_for_ssvms( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id, + ) + + # Poll and check the status of VMs + timeout = self.services["timeout"] + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + self.debug( + "VM state after enabling maintenance on first host: %s" % + vm.state) + if vm.state in ["Stopping", "Stopped", "Running", "Starting"]: + if vm.state == "Running": + break + else: + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + else: + self.fail( + "VM migration from one-host-to-other failed while enabling maintenance" + ) + + for vm in vms: + self.debug( + "VM states after enabling maintenance mode on host: %s - %s" % + (first_host, vm.state)) + self.assertEqual( + vm.state, + "Running", + "Deployed VM should be in Running state" + ) + # Spawn an instance on other host + virtual_machine_3 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_3.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + + self.debug("Deployed VM on host: %s" % vm.hostid) + self.debug("VM 3 state: %s" % vm.state) + self.assertEqual( + vm.state, + "Running", + "Deployed VM should be in Running state" + ) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % virtual_machine.id) + ssh = virtual_machine.get_ssh_client( + ipaddress=public_ip.ipaddress.ipaddress) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (virtual_machine.ipaddress, e) + ) + + self.debug("Canceling host maintenance for ID: %s" % second_host) + cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() + cmd.id = second_host + self.apiclient.cancelHostMaintenance(cmd) + self.debug("Maintenance mode canceled for host: %s" % second_host) + self.debug("Waiting for SSVMs to come up") + wait_for_ssvms( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id, + ) + return + + def test_02_host_maintenance_mode_with_activities(self): + """Test host maintenance mode with activities + """ + + # Validate the following + # 1. Create Vms. Acquire IP. Create port forwarding & load balancing + # rules for Vms. + # 2. While activities are ongoing: Create snapshots, recurring + # snapshots, create templates, download volumes, Host 1: put to + # maintenance mode. All Vms should failover to Host 2 in cluster + # Vms should be in running state. All port forwarding rules and + # load balancing Rules should work. + # 3. After failover to Host 2 succeeds, deploy Vms. Deploy Vms on host + # 2 should succeed. All ongoing activities in step 3 should succeed + # 4. Host 1: cancel maintenance mode. + # 5. While activities are ongoing: Create snapshots, recurring + # snapshots, create templates, download volumes, Host 2: put to + # maintenance mode. All Vms should failover to Host 1 in cluster. + # 6. After failover to Host 1 succeeds, deploy VMs. Deploy Vms on + # host 1 should succeed. All ongoing activities in step 6 should + # succeed. + + hosts = Host.list( + self.apiclient, + zoneid=self.zone.id, + resourcestate='Enabled', + type='Routing' + ) + self.assertEqual( + isinstance(hosts, list), + True, + "List hosts should return valid host response" + ) + self.assertEqual( + len(hosts), + 2, + "There must be two hosts present in a cluster" + ) + self.debug("Checking HA with hosts: %s, %s" % ( + hosts[0].name, + hosts[1].name + )) + self.debug("Deploying VM in account: %s" % self.account.account.name) + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + self.debug("Deployed VM on host: %s" % vm.hostid) + self.assertEqual( + vm.state, + "Running", + "Deployed VM should be in RUnning state" + ) + networks = Network.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List networks should return valid list for the account" + ) + network = networks[0] + + self.debug("Associating public IP for account: %s" % + self.account.account.name) + public_ip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=network.id + ) + + self.debug("Associated %s with network %s" % ( + public_ip.ipaddress.ipaddress, + network.id + )) + self.debug("Creating PF rule for IP address: %s" % + public_ip.ipaddress.ipaddress) + nat_rule= NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=public_ip.ipaddress.id + ) + + self.debug("Creating LB rule on IP with NAT: %s" % + public_ip.ipaddress.ipaddress) + + # Create Load Balancer rule on IP already having NAT rule + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=public_ip.ipaddress.id, + accountid=self.account.account.name + ) + self.debug("Created LB rule with ID: %s" % lb_rule.id) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % virtual_machine.id) + ssh = virtual_machine.get_ssh_client( + ipaddress=public_ip.ipaddress.ipaddress) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (virtual_machine.ipaddress, e) + ) + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=virtual_machine.id, + type='ROOT', + listall=True + ) + volume = volumes[0] + self.debug( + "Root volume of VM(%s): %s" % ( + virtual_machine.name, + volume.name + )) + # Create a snapshot from the ROOTDISK + self.debug("Creating snapshot on ROOT volume: %s" % volume.name) + snapshot = Snapshot.create(self.apiclient, volumes[0].id) + self.debug("Snapshot created: ID - %s" % snapshot.id) + + snapshots = list_snapshots( + self.apiclient, + id=snapshot.id, + listall=True + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list snapshots call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check snapshot id in list resources call" + ) + # Generate template from the snapshot + self.debug("Generating template from snapshot: %s" % snapshot.name) + template = Template.create_from_snapshot( + self.apiclient, + snapshot, + self.services["templates"] + ) + self.cleanup.append(template) + self.debug("Created template from snapshot: %s" % template.id) + + templates = list_templates( + self.apiclient, + templatefilter=\ + self.services["templates"]["templatefilter"], + id=template.id + ) + + self.assertEqual( + isinstance(templates, list), + True, + "List template call should return the newly created template" + ) + + self.assertEqual( + templates[0].isready, + True, + "The newly created template should be in ready state" + ) + + first_host = vm.hostid + self.debug("Enabling maintenance mode for host %s" % vm.hostid) + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = first_host + self.apiclient.prepareHostForMaintenance(cmd) + + self.debug("Waiting for SSVMs to come up") + wait_for_ssvms( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id, + ) + + timeout = self.services["timeout"] + # Poll and check state of VM while it migrates from one host to another + while True: + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + + self.debug("VM 1 state: %s" % vm.state) + if vm.state in ["Stopping", "Stopped", "Running", "Starting"]: + if vm.state == "Running": + break + else: + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + else: + self.fail( + "VM migration from one-host-to-other failed while enabling maintenance" + ) + second_host = vm.hostid + self.assertEqual( + vm.state, + "Running", + "VM should be in Running state after enabling host maintenance" + ) + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % virtual_machine.id) + ssh = virtual_machine.get_ssh_client( + ipaddress=public_ip.ipaddress.ipaddress) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (virtual_machine.ipaddress, e) + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + # Spawn an instance on other host + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_2.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + self.debug("Deployed VM on host: %s" % vm.hostid) + self.debug("VM 2 state: %s" % vm.state) + self.assertEqual( + vm.state, + "Running", + "Deployed VM should be in Running state" + ) + + self.debug("Canceling host maintenance for ID: %s" % first_host) + cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() + cmd.id = first_host + self.apiclient.cancelHostMaintenance(cmd) + self.debug("Maintenance mode canceled for host: %s" % first_host) + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=virtual_machine_2.id, + type='ROOT', + listall=True + ) + volume = volumes[0] + self.debug( + "Root volume of VM(%s): %s" % ( + virtual_machine_2.name, + volume.name + )) + # Create a snapshot from the ROOTDISK + self.debug("Creating snapshot on ROOT volume: %s" % volume.name) + snapshot = Snapshot.create(self.apiclient, volumes[0].id) + self.debug("Snapshot created: ID - %s" % snapshot.id) + + snapshots = list_snapshots( + self.apiclient, + id=snapshot.id, + listall=True + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list snapshots call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check snapshot id in list resources call" + ) + # Generate template from the snapshot + self.debug("Generating template from snapshot: %s" % snapshot.name) + template = Template.create_from_snapshot( + self.apiclient, + snapshot, + self.services["templates"] + ) + self.cleanup.append(template) + self.debug("Created template from snapshot: %s" % template.id) + + templates = list_templates( + self.apiclient, + templatefilter=\ + self.services["templates"]["templatefilter"], + id=template.id + ) + + self.assertEqual( + isinstance(templates, list), + True, + "List template call should return the newly created template" + ) + + self.assertEqual( + templates[0].isready, + True, + "The newly created template should be in ready state" + ) + + self.debug("Enabling maintenance mode for host %s" % second_host) + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = second_host + self.apiclient.prepareHostForMaintenance(cmd) + self.debug("Maintenance mode enabled for host: %s" % second_host) + + self.debug("Waiting for SSVMs to come up") + wait_for_ssvms( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id, + ) + + # Poll and check the status of VMs + timeout = self.services["timeout"] + while True: + vms = VirtualMachine.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + self.debug( + "VM state after enabling maintenance on first host: %s" % + vm.state) + if vm.state in ["Stopping", "Stopped", "Running", "Starting"]: + if vm.state == "Running": + break + else: + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + else: + self.fail( + "VM migration from one-host-to-other failed while enabling maintenance" + ) + + for vm in vms: + self.debug( + "VM states after enabling maintenance mode on host: %s - %s" % + (first_host, vm.state)) + self.assertEqual( + vm.state, + "Running", + "Deployed VM should be in Running state" + ) + # Spawn an instance on other host + virtual_machine_3 = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine_3.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "List VMs should return valid response for deployed VM" + ) + self.assertNotEqual( + len(vms), + 0, + "List VMs should return valid response for deployed VM" + ) + vm = vms[0] + + self.debug("Deployed VM on host: %s" % vm.hostid) + self.debug("VM 3 state: %s" % vm.state) + self.assertEqual( + vm.state, + "Running", + "Deployed VM should be in Running state" + ) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % virtual_machine.id) + ssh = virtual_machine.get_ssh_client( + ipaddress=public_ip.ipaddress.ipaddress) + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (virtual_machine.ipaddress, e) + ) + + self.debug("Canceling host maintenance for ID: %s" % second_host) + cmd = cancelHostMaintenance.cancelHostMaintenanceCmd() + cmd.id = second_host + self.apiclient.cancelHostMaintenance(cmd) + self.debug("Maintenance mode canceled for host: %s" % second_host) + self.debug("Waiting for SSVMs to come up") + wait_for_ssvms( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id, + ) + return diff --git a/test/integration/component/test_network_offering.py b/test/integration/component/test_network_offering.py new file mode 100644 index 00000000000..b536da11a2d --- /dev/null +++ b/test/integration/component/test_network_offering.py @@ -0,0 +1,1783 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 + +""" P1 tests for network offering +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +import datetime + + +class Services: + """Test network offering Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "network_offering": { + "name": 'Network offering-VR services', + "displaytext": 'Network offering-VR services', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Firewall,Lb,UserData,StaticNat', + "traffictype": 'GUEST', + "availability": 'Optional', + "serviceProviderList": { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + "Vpn": 'VirtualRouter', + "Firewall": 'VirtualRouter', + "Lb": 'VirtualRouter', + "UserData": 'VirtualRouter', + "StaticNat": 'VirtualRouter', + }, + }, + "network_offering_netscaler": { + "name": 'Network offering-netscaler', + "displaytext": 'Network offering-netscaler', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding,Vpn,Firewall,Lb,UserData,StaticNat', + "traffictype": 'GUEST', + "availability": 'Optional', + "serviceProviderList": { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + "Vpn": 'VirtualRouter', + "Firewall": 'VirtualRouter', + "Lb": 'Netscaler', + "UserData": 'VirtualRouter', + "StaticNat": 'VirtualRouter', + }, + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + }, + "lbrule": { + "name": "SSH", + "alg": "leastconn", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + "openfirewall": False, + }, + "lbrule_port_2221": { + "name": "SSH", + "alg": "leastconn", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2221, + "openfirewall": False, + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "protocol": "TCP" + }, + "natrule_port_66": { + "privateport": 22, + "publicport": 66, + "protocol": "TCP" + }, + "fw_rule": { + "startport": 1, + "endport": 6000, + "cidr": '55.55.0.0/11', + # Any network (For creating FW rule) + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostypeid": '9958b10f-9e5d-4ef1-908d-a047372d823b', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode": 'advanced' + } + + +class TestNOVirtualRouter(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestNOVirtualRouter, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup = [ + cls.service_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup = [] + return + + def tearDown(self): + try: + self.account.delete(self.apiclient) + interval = list_configurations( + self.apiclient, + name='account.cleanup.interval' + ) + # Sleep to ensure that all resources are deleted + time.sleep(int(interval[0].value) * 2) + #Clean up, terminate the created network offerings + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_network_off_without_conserve_mode(self): + """Test Network offering with Conserve mode off and VR - All services + """ + + # Validate the following + # 1. Create a Network from the above network offering and deploy a VM. + # 2. On source NAT ipaddress, we should NOT be allowed to add a + # LB rules + # 3. On source NAT ipaddress, we should be NOT be allowed to add + # PF rule + # 4. On an ipaddress that has PF rules, we should NOT be allowed to + # add a LB rules. + # 5. On an ipaddress that has Lb rules, we should NOT allow PF rules + # to be programmed. + # 6. We should be allowed to program multiple PF rules on the same Ip + # address on different public ports. + # 7. We should be allowed to program multiple LB rules on the same Ip + # address for different public port ranges. + # 8. On source NAT ipaddress, we should be allowed to Enable VPN. + # 9. On SOurce NAT ipaddress, we will be allowed to add firewall rule + + # Create a network offering with all virtual router services enabled + self.debug( + "Creating n/w offering with all services in VR & conserve mode:off" + ) + self.network_offering = NetworkOffering.create( + self.api_client, + self.services["network_offering"], + conservemode=False + ) + self.cleanup.append(self.network_offering) + + self.debug("Created n/w offering with ID: %s" % + self.network_offering.id) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % self.network.id) + + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(self.network.id)] + ) + self.debug("Deployed VM in network: %s" % self.network.id) + src_nat_list = PublicIPAddress.list( + self.apiclient, + associatednetworkid=self.network.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True, + issourcenat=True, + ) + self.assertEqual( + isinstance(src_nat_list, list), + True, + "List Public IP should return a valid source NAT" + ) + self.assertNotEqual( + len(src_nat_list), + 0, + "Length of response from listPublicIp should not be 0" + ) + src_nat = src_nat_list[0] + self.debug("Trying to create LB rule on source NAT IP: %s" % + src_nat.ipaddress) + # Create Load Balancer rule with source NAT + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=src_nat.id, + accountid=self.account.account.name + ) + self.debug( + "Trying to create a port forwarding rule in source NAT: %s" % + src_nat.ipaddress) + #Create NAT rule + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=src_nat.id + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_nat_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=self.network.id + ) + + self.debug("Associated %s with network %s" % ( + ip_with_nat_rule.ipaddress.ipaddress, + self.network.id + )) + self.debug("Creating PF rule for IP address: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + self.debug("Trying to create LB rule on IP with NAT: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + + # Create Load Balancer rule on IP already having NAT rule + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id, + accountid=self.account.account.name + ) + self.debug("Creating PF rule with public port: 66") + + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule_port_66"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + # Check if NAT rule created successfully + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT rules should return valid list" + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_lb_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=self.network.id + ) + self.debug("Associated %s with network %s" % ( + ip_with_lb_rule.ipaddress.ipaddress, + self.network.id + )) + self.debug("Creating LB rule for IP address: %s" % + ip_with_lb_rule.ipaddress.ipaddress) + + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.account.name + ) + + self.debug("Trying to create PF rule on IP with LB rule: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id + ) + + self.debug("Creating LB rule with public port: 2221") + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule_port_2221"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.account.name + ) + + # Check if NAT rule created successfully + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + + self.assertEqual( + isinstance(lb_rules, list), + True, + "List LB rules should return valid list" + ) + + self.debug("Creating firewall rule on source NAT: %s" % + src_nat.ipaddress) + #Create Firewall rule on source NAT + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + self.debug("Created firewall rule: %s" % fw_rule.id) + + fw_rules = FireWallRule.list( + self.apiclient, + id=fw_rule.id + ) + self.assertEqual( + isinstance(fw_rules, list), + True, + "List fw rules should return a valid firewall rules" + ) + + self.assertNotEqual( + len(fw_rules), + 0, + "Length of fw rules response should not be zero" + ) + return + + def test_02_network_off_with_conserve_mode(self): + """Test Network offering with Conserve mode ON and VR - All services + """ + + # Validate the following + # 1. Create a Network from the above network offering and deploy a VM. + # 2. On source NAT ipaddress, we should be allowed to add a LB rules + # 3. On source NAT ipaddress, we should be allowed to add a PF rules + # 4. On source NAT ipaddress, we should be allowed to add a Firewall + # rules + # 5. On an ipaddress that has Lb rules, we should be allowed to + # program PF rules. + # 6. We should be allowed to program multiple PF rules on the same Ip + # address on different public ports. + # 7. We should be allowed to program multiple LB rules on the same Ip + # address for different public port ranges. + # 8. On source NAT ipaddress, we should be allowed to Enable VPN + # access. + + # Create a network offering with all virtual router services enabled + self.debug( + "Creating n/w offering with all services in VR & conserve mode:off" + ) + self.network_offering = NetworkOffering.create( + self.api_client, + self.services["network_offering"], + conservemode=True + ) + self.cleanup.append(self.network_offering) + + self.debug("Created n/w offering with ID: %s" % + self.network_offering.id) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % self.network.id) + + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(self.network.id)] + ) + self.debug("Deployed VM in network: %s" % self.network.id) + src_nat_list = PublicIPAddress.list( + self.apiclient, + associatednetworkid=self.network.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True, + issourcenat=True, + ) + self.assertEqual( + isinstance(src_nat_list, list), + True, + "List Public IP should return a valid source NAT" + ) + self.assertNotEqual( + len(src_nat_list), + 0, + "Length of response from listPublicIp should not be 0" + ) + + src_nat = src_nat_list[0] + + self.debug("Trying to create LB rule on source NAT IP: %s" % + src_nat.ipaddress) + # Create Load Balancer rule with source NAT + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=src_nat.id, + accountid=self.account.account.name + ) + self.debug("Created LB rule on source NAT: %s" % src_nat.ipaddress) + + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + self.assertEqual( + isinstance(lb_rules, list), + True, + "List lb rules should return a valid lb rules" + ) + self.assertNotEqual( + len(lb_rules), + 0, + "Length of response from listLbRules should not be 0" + ) + + self.debug( + "Trying to create a port forwarding rule in source NAT: %s" % + src_nat.ipaddress) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=src_nat.id + ) + self.debug("Created PF rule on source NAT: %s" % src_nat.ipaddress) + + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT should return a valid port forwarding rules" + ) + self.assertNotEqual( + len(nat_rules), + 0, + "Length of response from listLbRules should not be 0" + ) + self.debug("Creating firewall rule on source NAT: %s" % + src_nat.ipaddress) + #Create Firewall rule on source NAT + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + self.debug("Created firewall rule: %s" % fw_rule.id) + + fw_rules = FireWallRule.list( + self.apiclient, + id=fw_rule.id + ) + self.assertEqual( + isinstance(fw_rules, list), + True, + "List fw rules should return a valid firewall rules" + ) + + self.assertNotEqual( + len(fw_rules), + 0, + "Length of fw rules response should not be zero" + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + public_ip = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=self.network.id + ) + + self.debug("Associated %s with network %s" % ( + public_ip.ipaddress.ipaddress, + self.network.id + )) + self.debug("Creating PF rule for IP address: %s" % + public_ip.ipaddress.ipaddress) + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=public_ip.ipaddress.id + ) + + self.debug("Trying to create LB rule on IP with NAT: %s" % + public_ip.ipaddress.ipaddress) + + # Create Load Balancer rule on IP already having NAT rule + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=public_ip.ipaddress.id, + accountid=self.account.account.name + ) + self.debug("Creating PF rule with public port: 66") + + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule_port_66"], + ipaddressid=public_ip.ipaddress.id + ) + + # Check if NAT rule created successfully + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT rules should return valid list" + ) + + self.debug("Creating LB rule with public port: 2221") + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule_port_2221"], + ipaddressid=public_ip.ipaddress.id, + accountid=self.account.account.name + ) + + # Check if NAT rule created successfully + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + + self.assertEqual( + isinstance(lb_rules, list), + True, + "List LB rules should return valid list" + ) + + # User should be able to enable VPN on source NAT + self.debug("Created VPN with source NAT IP: %s" % src_nat.ipaddress) + # Assign VPN to source NAT + vpn = Vpn.create( + self.apiclient, + src_nat.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + vpns = Vpn.list( + self.apiclient, + publicipid=src_nat.id, + listall=True, + ) + + self.assertEqual( + isinstance(vpns, list), + True, + "List VPNs should return a valid VPN list" + ) + + self.assertNotEqual( + len(vpns), + 0, + "Length of list VPN response should not be zero" + ) + return + + +class TestNOWithNetscaler(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestNOWithNetscaler, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup = [ + cls.service_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup = [] + return + + def tearDown(self): + try: + self.account.delete(self.apiclient) + interval = list_configurations( + self.apiclient, + name='account.cleanup.interval' + ) + # Sleep to ensure that all resources are deleted + time.sleep(int(interval[0].value) * 2) + #Clean up, terminate the created network offerings + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_network_off_without_conserve_mode(self): + """Test Nw off with Conserve mode off, VR-All services, LB-netscaler + """ + + # Validate the following + # 1. Create a Network from the above network offering and deploy a VM. + # 2. On source NAT ipaddress, we should NOT be allowed to add LB rule + # 3. On source NAT ipaddress, we should NOT be allowed to add PF rule + # 4. On an ipaddress that has PF rules, we should NOT be allowed to + # add a LB rules. + # 5. On an ipaddress that has Lb rules , we should NOT allow firewall + # rules to be programmed. + # 6. On an ipaddress that has Lb rules , we should NOT allow PF rules + # to be programmed. + # 7. We should be allowed to program multiple PF rules on the same Ip + # address on different public ports. + # 8. We should be allowed to program multiple LB rules on the same Ip + # address for different public port ranges. + # 9. On source NAT ipaddress, we should NOT be allowed to Enable VPN. + + # Create a network offering with all virtual router services enabled + self.debug( + "Creating n/w offering with all services in VR & conserve mode:ON" + ) + self.network_offering = NetworkOffering.create( + self.api_client, + self.services["network_offering_netscaler"], + conservemode=False + ) + self.cleanup.append(self.network_offering) + + self.debug("Created n/w offering with ID: %s" % + self.network_offering.id) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % self.network.id) + + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(self.network.id)] + ) + self.debug("Deployed VM in network: %s" % self.network.id) + src_nat_list = PublicIPAddress.list( + self.apiclient, + associatednetworkid=self.network.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True, + issourcenat=True, + ) + self.assertEqual( + isinstance(src_nat_list, list), + True, + "List Public IP should return a valid source NAT" + ) + self.assertNotEqual( + len(src_nat_list), + 0, + "Length of response from listPublicIp should not be 0" + ) + + src_nat = src_nat_list[0] + + self.debug("Trying to create LB rule on source NAT IP: %s" % + src_nat.ipaddress) + # Create Load Balancer rule with source NAT + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=src_nat.id, + accountid=self.account.account.name + ) + + self.debug( + "Trying to create a port forwarding rule in source NAT: %s" % + src_nat.ipaddress) + #Create NAT rule + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=src_nat.id + ) + self.debug("Creating firewall rule on source NAT: %s" % + src_nat.ipaddress) + #Create Firewall rule on source NAT + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + + self.debug("Created firewall rule: %s" % fw_rule.id) + + fw_rules = FireWallRule.list( + self.apiclient, + id=fw_rule.id + ) + self.assertEqual( + isinstance(fw_rules, list), + True, + "List fw rules should return a valid firewall rules" + ) + + self.assertNotEqual( + len(fw_rules), + 0, + "Length of fw rules response should not be zero" + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_nat_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=self.network.id + ) + + self.debug("Associated %s with network %s" % ( + ip_with_nat_rule.ipaddress.ipaddress, + self.network.id + )) + self.debug("Creating PF rule for IP address: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + self.debug("Trying to create LB rule on IP with NAT: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + + # Create Load Balancer rule on IP already having NAT rule + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id, + accountid=self.account.account.name + ) + self.debug("Creating PF rule with public port: 66") + + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule_port_66"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + # Check if NAT rule created successfully + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT rules should return valid list" + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_lb_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=self.network.id + ) + self.debug("Associated %s with network %s" % ( + ip_with_lb_rule.ipaddress.ipaddress, + self.network.id + )) + self.debug("Creating LB rule for IP address: %s" % + ip_with_lb_rule.ipaddress.ipaddress) + + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.account.name, + networkid=self.network.id + ) + + self.debug("Trying to create PF rule on IP with LB rule: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id + ) + + self.debug("Trying to create FW rule on IP with LB rule") + with self.assertRaises(Exception): + FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + + self.debug("Creating LB rule with public port: 2221") + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule_port_2221"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.account.name, + networkid=self.network.id + ) + + # Check if NAT rule created successfully + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + + self.assertEqual( + isinstance(lb_rules, list), + True, + "List LB rules should return valid list" + ) + + # User should be able to enable VPN on source NAT + self.debug("Enabling VPN on source NAT IP: %s" % src_nat.ipaddress) + # Assign VPN to source NAT + with self.assertRaises(Exception): + Vpn.create( + self.apiclient, + src_nat.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + return + + def test_02_network_off_with_conserve_mode_netscaler(self): + """Test NW off with Conserve mode ON, LB-Netscaler and VR-All services + """ + + # Validate the following + # 1. Create a Network from the above network offering and deploy a VM. + # 2. On source NAT ipaddress, we should NOT be allowed to add LB rule + # 3. On source NAT ipaddress, we should be allowed to add PF rule and + # Fierwall rules. + # 4. On an ipaddress that has PF rules, we should NOT be allowed to + # add a LB rules. + # 5. On an ipaddress that has Lb rules , we should NOT allow firewall + # rules to be programmed. + # 6. On an ipaddress that has Lb rules , we should NOT allow PF rules + # to be programmed. + # 7. We should be allowed to program multiple PF rules on the same Ip + # address on different public ports. + # 8. We should be allowed to program multiple LB rules on the same Ip + # address for different public port ranges. + # 9. On source NAT ipaddress, we should be allowed to Enable VPN. + + # Create a network offering with all virtual router services enabled + self.debug( + "Creating n/w offering with all services in VR & conserve mode:ON" + ) + self.network_offering = NetworkOffering.create( + self.api_client, + self.services["network_offering_netscaler"], + conservemode=True + ) + self.cleanup.append(self.network_offering) + + self.debug("Created n/w offering with ID: %s" % + self.network_offering.id) + # Enable Network offering + self.network_offering.update(self.apiclient, state='Enabled') + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % self.network.id) + + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(self.network.id)] + ) + self.debug("Deployed VM in network: %s" % self.network.id) + src_nat_list = PublicIPAddress.list( + self.apiclient, + associatednetworkid=self.network.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True, + issourcenat=True, + ) + self.assertEqual( + isinstance(src_nat_list, list), + True, + "List Public IP should return a valid source NAT" + ) + self.assertNotEqual( + len(src_nat_list), + 0, + "Length of response from listPublicIp should not be 0" + ) + + src_nat = src_nat_list[0] + + self.debug("Trying to create LB rule on source NAT IP: %s" % + src_nat.ipaddress) + # Create Load Balancer rule with source NAT + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=src_nat.id, + accountid=self.account.account.name + ) + + self.debug( + "Trying to create a port forwarding rule in source NAT: %s" % + src_nat.ipaddress) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=src_nat.id + ) + self.debug("Created PF rule on source NAT: %s" % src_nat.ipaddress) + + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT should return a valid port forwarding rules" + ) + self.assertNotEqual( + len(nat_rules), + 0, + "Length of response from listLbRules should not be 0" + ) + self.debug("Creating firewall rule on source NAT: %s" % + src_nat.ipaddress) + #Create Firewall rule on source NAT + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + self.debug("Created firewall rule: %s" % fw_rule.id) + + fw_rules = FireWallRule.list( + self.apiclient, + id=fw_rule.id + ) + self.assertEqual( + isinstance(fw_rules, list), + True, + "List fw rules should return a valid firewall rules" + ) + + self.assertNotEqual( + len(fw_rules), + 0, + "Length of fw rules response should not be zero" + ) + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_nat_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=self.network.id + ) + + self.debug("Associated %s with network %s" % ( + ip_with_nat_rule.ipaddress.ipaddress, + self.network.id + )) + self.debug("Creating PF rule for IP address: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + self.debug("Trying to create LB rule on IP with NAT: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + + # Create Load Balancer rule on IP already having NAT rule + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_nat_rule.ipaddress.id, + accountid=self.account.account.name + ) + self.debug("Creating PF rule with public port: 66") + + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule_port_66"], + ipaddressid=ip_with_nat_rule.ipaddress.id + ) + + # Check if NAT rule created successfully + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT rules should return valid list" + ) + + self.debug("Associating public IP for network: %s" % self.network.id) + ip_with_lb_rule = PublicIPAddress.create( + self.apiclient, + accountid=self.account.account.name, + zoneid=self.zone.id, + domainid=self.account.account.domainid, + networkid=self.network.id + ) + self.debug("Associated %s with network %s" % ( + ip_with_lb_rule.ipaddress.ipaddress, + self.network.id + )) + self.debug("Creating LB rule for IP address: %s" % + ip_with_lb_rule.ipaddress.ipaddress) + + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.account.name, + networkid=self.network.id + ) + + self.debug("Trying to create PF rule on IP with LB rule: %s" % + ip_with_nat_rule.ipaddress.ipaddress) + + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=ip_with_lb_rule.ipaddress.id + ) + + self.debug("Trying to create FW rule on IP with LB rule") + with self.assertRaises(Exception): + FireWallRule.create( + self.apiclient, + ipaddressid=src_nat.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + + self.debug("Creating LB rule with public port: 2221") + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule_port_2221"], + ipaddressid=ip_with_lb_rule.ipaddress.id, + accountid=self.account.account.name, + networkid=self.network.id + ) + + # Check if NAT rule created successfully + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + + self.assertEqual( + isinstance(lb_rules, list), + True, + "List LB rules should return valid list" + ) + + # User should be able to enable VPN on source NAT + self.debug("Created VPN with source NAT IP: %s" % src_nat.ipaddress) + # Assign VPN to source NAT + vpn = Vpn.create( + self.apiclient, + src_nat.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + vpns = Vpn.list( + self.apiclient, + publicipid=src_nat.id, + listall=True, + ) + + self.assertEqual( + isinstance(vpns, list), + True, + "List VPNs should return a valid VPN list" + ) + + self.assertNotEqual( + len(vpns), + 0, + "Length of list VNP response should not be zero" + ) + return + + +class TestNetworkUpgrade(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestNetworkUpgrade, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = cls.template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.network_offering = NetworkOffering.create( + cls.api_client, + cls.services["network_offering"], + conservemode=True + ) + # Enable Network offering + cls.network_offering.update(cls.api_client, state='Enabled') + + cls._cleanup = [ + cls.service_offering, + cls.network_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup = [] + return + + def tearDown(self): + try: + self.account.delete(self.apiclient) + interval = list_configurations( + self.apiclient, + name='account.cleanup.interval' + ) + # Sleep to ensure that all resources are deleted + time.sleep(int(interval[0].value) * 2) + #Clean up, terminate the created network offerings + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_nwupgrade_netscaler_conserve_on(self): + """Test Nw upgrade to netscaler lb service and conserve mode ON + """ + + # Validate the following + # 1. Upgrade a network with VR and conserve mode ON TO + # A network that has Lb provided by "Netscaler" and all other + # services provided by "VR" and Conserve mode ON + # 2. Have PF and LB rules on the same ip address. Upgrade network + # should fail. + # 3. Have SourceNat,PF and VPN on the same IP address. Upgrade of + # network should succeed. + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % self.network.id) + + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(self.network.id)] + ) + self.debug("Deployed VM in network: %s" % self.network.id) + src_nat_list = PublicIPAddress.list( + self.apiclient, + associatednetworkid=self.network.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True, + issourcenat=True, + ) + self.assertEqual( + isinstance(src_nat_list, list), + True, + "List Public IP should return a valid source NAT" + ) + self.assertNotEqual( + len(src_nat_list), + 0, + "Length of response from listPublicIp should not be 0" + ) + + src_nat = src_nat_list[0] + self.debug("Trying to create LB rule on source NAT IP: %s" % + src_nat.ipaddress) + # Create Load Balancer rule with source NAT + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=src_nat.id, + accountid=self.account.account.name + ) + self.debug("Created LB rule on source NAT: %s" % src_nat.ipaddress) + + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + self.assertEqual( + isinstance(lb_rules, list), + True, + "List lb rules should return a valid lb rules" + ) + self.assertNotEqual( + len(lb_rules), + 0, + "Length of response from listLbRules should not be 0" + ) + + self.debug( + "Trying to create a port forwarding rule in source NAT: %s" % + src_nat.ipaddress) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=src_nat.id + ) + self.debug("Created PF rule on source NAT: %s" % src_nat.ipaddress) + + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT should return a valid port forwarding rules" + ) + self.assertNotEqual( + len(nat_rules), + 0, + "Length of response from listLbRules should not be 0" + ) + # Create a network offering with all virtual router services enabled + self.debug( + "Creating n/w offering with all services in VR & conserve mode:ON LB- Netscaler" + ) + ns_lb_offering = NetworkOffering.create( + self.api_client, + self.services["network_offering_netscaler"], + conservemode=True + ) + self.cleanup.append(ns_lb_offering) + ns_lb_offering.update(self.apiclient, state='Enabled') + #Stop all the VMs associated with network to update cidr + self.debug("Stopping the VM: %s" % virtual_machine.name) + virtual_machine.stop(self.apiclient) + + self.debug("Updating network offering for network: %s" % + self.network.id) + with self.assertRaises(Exception): + self.network.update( + self.apiclient, + networkofferingid=ns_lb_offering.id, + changecidr=True + ) + + self.debug("Network upgrade failed!") + self.debug("Deleting LB Rule: %s" % lb_rule.id) + lb_rule.delete(self.apiclient) + self.debug("LB rule deleted") + + # Assign VPN to source NAT + self.debug("Enabling VPN on source NAT") + vpn = Vpn.create( + self.apiclient, + src_nat.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + vpns = Vpn.list( + self.apiclient, + publicipid=src_nat.id, + listall=True, + ) + + self.assertEqual( + isinstance(vpns, list), + True, + "List VPNs should return a valid VPN list" + ) + + self.assertNotEqual( + len(vpns), + 0, + "Length of list VPN response should not be zero" + ) + self.debug("Upgrading the network: %s" % self.network.id) + self.network.update( + self.apiclient, + networkofferingid=ns_lb_offering.id, + changecidr=True + ) + networks = Network.list( + self.apiclient, + id=self.network.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "List Networks should return a valid list for given network ID" + ) + self.assertNotEqual( + len(networks), + 0, + "Length of list networks should not be 0" + ) + network = networks[0] + self.assertEqual( + network.networkofferingid, + ns_lb_offering.id, + "Network offering ID should match with new offering ID" + ) + return + + def test_02_nwupgrade_netscaler_conserve_off(self): + """Test Nw upgrade to netscaler lb service and conserve mode OFF + """ + + # Validate the following + # 1. Upgrade a network with VR and conserve mode ON TO + # A network that has Lb provided by "Netscaler" and all other + # services provided by "VR" and Conserve mode OFF + # 2. Have PF and LB rules on the same ip address. Upgrade network + # should fail. + # 3. Have SourceNat,PF and VPN on the same IP address. Upgrade of + # network should fail. + + # Creating network using the network offering created + self.debug("Creating network with network offering: %s" % + self.network_offering.id) + self.network = Network.create( + self.apiclient, + self.services["network"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + networkofferingid=self.network_offering.id, + zoneid=self.zone.id + ) + self.debug("Created network with ID: %s" % self.network.id) + + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Spawn an instance in that network + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + networkids=[str(self.network.id)] + ) + self.debug("Deployed VM in network: %s" % self.network.id) + src_nat_list = PublicIPAddress.list( + self.apiclient, + associatednetworkid=self.network.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + listall=True, + issourcenat=True, + ) + self.assertEqual( + isinstance(src_nat_list, list), + True, + "List Public IP should return a valid source NAT" + ) + self.assertNotEqual( + len(src_nat_list), + 0, + "Length of response from listPublicIp should not be 0" + ) + + src_nat = src_nat_list[0] + self.debug("Trying to create LB rule on source NAT IP: %s" % + src_nat.ipaddress) + # Create Load Balancer rule with source NAT + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + ipaddressid=src_nat.id, + accountid=self.account.account.name + ) + self.debug("Created LB rule on source NAT: %s" % src_nat.ipaddress) + + lb_rules = LoadBalancerRule.list( + self.apiclient, + id=lb_rule.id + ) + self.assertEqual( + isinstance(lb_rules, list), + True, + "List lb rules should return a valid lb rules" + ) + self.assertNotEqual( + len(lb_rules), + 0, + "Length of response from listLbRules should not be 0" + ) + + self.debug( + "Trying to create a port forwarding rule in source NAT: %s" % + src_nat.ipaddress) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + virtual_machine, + self.services["natrule"], + ipaddressid=src_nat.id + ) + self.debug("Created PF rule on source NAT: %s" % src_nat.ipaddress) + + nat_rules = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(nat_rules, list), + True, + "List NAT should return a valid port forwarding rules" + ) + self.assertNotEqual( + len(nat_rules), + 0, + "Length of response from listLbRules should not be 0" + ) + # Create a network offering with all virtual router services enabled + self.debug( + "Creating n/w offering with all services in VR & conserve mode:ON LB- Netscaler" + ) + ns_lb_offering = NetworkOffering.create( + self.api_client, + self.services["network_offering_netscaler"], + conservemode=False + ) + self.cleanup.append(ns_lb_offering) + ns_lb_offering.update(self.apiclient, state='Enabled') + #Stop all the VMs associated with network to update cidr + self.debug("Stopping the VM: %s" % virtual_machine.name) + virtual_machine.stop(self.apiclient) + + self.debug("Updating network offering for network: %s" % + self.network.id) + with self.assertRaises(Exception): + self.network.update( + self.apiclient, + networkofferingid=ns_lb_offering.id, + changecidr=True + ) + + self.debug("Network upgrade failed!") + self.debug("Deleting LB Rule: %s" % lb_rule.id) + lb_rule.delete(self.apiclient) + self.debug("LB rule deleted") + + # Assign VPN to source NAT + self.debug("Enabling VPN on source NAT") + vpn = Vpn.create( + self.apiclient, + src_nat.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + vpns = Vpn.list( + self.apiclient, + publicipid=src_nat.id, + listall=True, + ) + + self.assertEqual( + isinstance(vpns, list), + True, + "List VPNs should return a valid VPN list" + ) + + self.assertNotEqual( + len(vpns), + 0, + "Length of list VPN response should not be zero" + ) + self.debug("Upgrading the network: %s" % self.network.id) + with self.assertRaises(Exception): + self.network.update( + self.apiclient, + networkofferingid=ns_lb_offering.id, + changecidr=True + ) + return diff --git a/test/integration/component/test_project_configs.py b/test/integration/component/test_project_configs.py new file mode 100644 index 00000000000..20e2f8af1ed --- /dev/null +++ b/test/integration/component/test_project_configs.py @@ -0,0 +1,1231 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Project +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +import datetime + + +class Services: + """Test Project Services + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "mgmt_server": { + "ipaddress": '192.168.100.21', + "username": 'root', + "password": 'fr3sca', + "port": 22, + }, + "account": { + "email": "administrator@clogeny.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "user": { + "email": "administrator@clogeny.com", + "firstname": "User", + "lastname": "User", + "username": "User", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "template": { + "displaytext": "Public Template", + "name": "Public template", + "ostypeid": 'f9b709f2-e0fc-4c0f-80f1-b0494168f58d', + "url": "http://download.cloud.com/releases/2.0.0/UbuntuServer-10-04-64bit.vhd.bz2", + "hypervisor": 'XenServer', + "format" : 'VHD', + "isfeatured": True, + "ispublic": True, + "isextractable": True, + }, + "configs": { + "project.invite.timeout": 300, + }, + "mail_account": { + "server": 'imap.gmail.com', + "email": 'administrator@clogeny.com', + "password": 'fr3sca21!', + "folder": 'inbox', + }, + "ostypeid": 'f9b709f2-e0fc-4c0f-80f1-b0494168f58d', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode":'advanced' + } + + +class TestUserProjectCreation(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestUserProjectCreation, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.zone = get_zone(cls.api_client, cls.services) + + # Create domains, account etc. + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.user = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls._cleanup = [cls.account, cls.user, cls.domain] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + + def test_01_admin_project_creation(self): + """Test create project as a domain admin and domain user + """ + + # Validate the following + # 1. Check if 'allow.user.create.projects' configuration is true + # 2. Create a Project as domain admin + # 3. Create a Project as domain user + # 4. In both 2 and 3 project creation should be successful + + configs = Configurations.list( + self.apiclient, + name='allow.user.create.projects' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'true', + "'allow.user.create.projects' should be true" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.user.account.name, + domainid=self.user.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain user with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + return + @unittest.skip("Known bug-able to create project as a domain user") + def test_02_user_project_creation(self): + """Test create project as a domain admin and domain user + """ + + # Validate the following + # 1. Check if 'allow.user.create.projects' configuration is false + # 2. Create a Project as domain admin. Project creation should be + # successful. + # 3. Create a Project as domain user. Project creation should fail + + configs = Configurations.list( + self.apiclient, + name='allow.user.create.projects' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'allow.user.create.projects' should be true" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + with self.assertRaises(Exception): + project = Project.create( + self.apiclient, + self.services["project"], + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.debug("Project creation with domain user: %s failed" % + self.user.account.name) + return + + +class TestProjectInviteRequired(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestProjectInviteRequired, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.zone = get_zone(cls.api_client, cls.services) + + # Create domains, account etc. + cls.domain = get_domain(cls.api_client, cls.services) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.user = Account.create( + cls.api_client, + cls.services["user"], + admin=True, + domainid=cls.domain.id + ) + + cls._cleanup = [cls.account, cls.user] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + + def test_03_add_user_to_project(self): + """Add user to project when 'project.invite.required' is false""" + + # Validate the following: + # 1. Create a Project + # 2. Add users to the project. Verify user is added to project + # as regular user + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be true" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.user.account.name, + ) + self.debug(accounts_reponse) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + + return + + def test_04_add_user_to_project(self): + """Add user to project when 'project.invite.required' is true""" + + # Validate the following: + # 1. Create a Project + # 2. Add users to the project. verify user is shown in pending state + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'true', + "'project.invite.required' should be true" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = ProjectInvitation.list( + self.apiclient, + state='Pending', + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.state, + 'Pending', + "Newly added user is not added as a regular user" + ) + return + + def test_05_invitation_timeout(self): + """Test global config project invitation timeout""" + + # Validate the following: + # 1. Set configuration to 5 mins + # 2. Create a Project + # 3. Add users to the project + # 4. As a user accept invitation within 5 mins. Verify invitation is + # accepted and user become regular user of project + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.timeout' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + int(config.value), + self.services["configs"]["project.invite.timeout"], + "'project.invite.timeout' should be %s" % + self.services["configs"]["project.invite.timeout"] + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = ProjectInvitation.list( + self.apiclient, + state='Pending', + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.state, + 'Pending', + "Newly added user is not added as a regular user" + ) + + # Accept the invite + ProjectInvitation.update( + self.apiclient, + projectid=project.id, + accept=True, + account=self.user.account.name + ) + self.debug( + "Accepting project invitation for project: %s user: %s" % ( + project.name, + self.user.account.name + )) + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.user.account.name, + ) + + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + return + + def test_06_invitation_timeout_after_expiry(self): + """Test global config project invitation timeout""" + + # Validate the following: + # 1. Set configuration to 5 mins + # 2. Create a Project + # 3. Add users to the project + # 4. As a user accept invitation after 5 mins. Verify invitation is + # not accepted and is shown as expired + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.timeout' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + int(config.value), + self.services["configs"]["project.invite.timeout"], + "'project.invite.timeout' should be %s" % + self.services["configs"]["project.invite.timeout"] + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = ProjectInvitation.list( + self.apiclient, + state='Pending', + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.state, + 'Pending', + "Newly added user is not added as a regular user" + ) + + # sleep for 'project.invite.timeout' * 2 interval to wait for invite + # to expire + time.sleep(int(config.value) * 2) + + with self.assertRaises(Exception): + # Accept the invite + ProjectInvitation.update( + self.apiclient, + projectid=project.id, + accept=True, + account=self.user.account.name + ) + self.debug( + "Accepting invitation after expiry project: %s user: %s" % ( + project.name, + self.user.account.name + )) + # listProjectAccount to verify the user is added to project or not + accounts_reponse = ProjectInvitation.list( + self.apiclient, + account=self.user.account.name, + domainid=self.user.account.domainid + ) + + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.state, + 'Expired', + "Newly added user is not added as a regular user" + ) + return + + def test_07_invite_after_expiry(self): + """Test global config project invitation timeout""" + + # Validate the following: + # 1. Set configuration to 5 mins + # 2. Create a Project + # 3. Add users to the project + # 4. As a user accept invitation after 5 mins. + # 5. Resend the invitation + # 6. Verify invitation is sent again + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.timeout' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + int(config.value), + self.services["configs"]["project.invite.timeout"], + "'project.invite.timeout' should be %s" % + self.services["configs"]["project.invite.timeout"] + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = ProjectInvitation.list( + self.apiclient, + state='Pending', + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.state, + 'Pending', + "Newly added user is not added as a regular user" + ) + + # sleep for 'project.invite.timeout' * 2 interval to wait for invite + # to expire + time.sleep(int(config.value) * 2) + + self.debug("Adding %s user again to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = ProjectInvitation.list( + self.apiclient, + state='Pending', + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.state, + 'Pending', + "Newly added user is not added as a regular user" + ) + return + + def test_08_decline_invitation(self): + """Test decline invitation""" + + # Validate the following: + # 1. Set configuration to 5 mins + # 2. Create a Project + # 3. Add users to the project + # 4. As a user decline invitation within 5 mins. + # 5. Verify invitation is rejected and user doesn't become regular + # user. + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.timeout' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + int(config.value), + self.services["configs"]["project.invite.timeout"], + "'project.invite.timeout' should be %s" % + self.services["configs"]["project.invite.timeout"] + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = ProjectInvitation.list( + self.apiclient, + state='Pending', + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.state, + 'Pending', + "Newly added user is not added as a regular user" + ) + # Accept the invite + ProjectInvitation.update( + self.apiclient, + projectid=project.id, + accept=False, + account=self.user.account.name + ) + self.debug( + "Declining invitation for project: %s user: %s" % ( + project.name, + self.user.account.name + )) + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.user.account.name, + ) + self.assertEqual( + accounts_reponse, + None, + "Check for a valid list accounts response" + ) + return + @unittest.skip("Requires SMPT configs") + def test_09_invite_to_project_by_email(self): + """Test invite user to project by email""" + + # Validate the following: + # 1. Set configuration to 5 mins + # 2. Create a Project + # 3. Add users to the project + # 4. As a user decline invitation within 5 mins. + # 5. Verify invitation is rejected and user doesn't become regular + # user. + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.timeout' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + int(config.value), + self.services["configs"]["project.invite.timeout"], + "'project.invite.timeout' should be %s" % + self.services["configs"]["project.invite.timeout"] + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding user with email: %s to project: %s" % ( + self.user.account.email, + project.name + )) + + # Add user to the project + project.addAccount( + self.apiclient, + email=self.user.account.user[0].email + ) + + # Fetch the latest mail sent to user + mail_content = fetch_latest_mail( + self.services["mail_account"], + from_mail=self.user.account.user[0].email + ) + return diff --git a/test/integration/component/test_project_limits.py b/test/integration/component/test_project_limits.py new file mode 100644 index 00000000000..c69cc3238fc --- /dev/null +++ b/test/integration/component/test_project_limits.py @@ -0,0 +1,881 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Resource limits +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +import datetime + +class Services: + """Test Resource Limits Services + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "account": { + "email": "administrator@clogeny.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "user": { + "email": "administrator@clogeny.com", + "firstname": "User", + "lastname": "User", + "username": "User", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Tiny Disk Offering", + "name": "Tiny Disk Offering", + "disksize": 1 + }, + "volume": { + "diskname": "Test Volume", + }, + "server": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "template": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "ostypeid": '471a4b5b-5523-448f-9608-7d6218995733', + "templatefilter": 'self', + }, + "ostypeid": '471a4b5b-5523-448f-9608-7d6218995733', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + } + + +class TestProjectLimits(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestProjectLimits, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.zone = get_zone(cls.api_client, cls.services) + + # Create domains, account etc. + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.admin = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.user = Account.create( + cls.api_client, + cls.services["user"], + domainid=cls.domain.id + ) + cls._cleanup = [ + cls.admin, + cls.user, + cls.domain + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_project_limits(self): + """ Test project limits + """ + + # Validate the following + # 1. Create a Project. Verify once projects are created, they inherit + # a default set of resource limits as configured by the Cloud Stack + # ROOT admin. + # 2. Reduce Project resources limits. Verify limits can be reduced by + # the Project Owner of each project and project limit applies to + # number of virtual instances, disk volumes, snapshots, IP address. + # Also, verify resource limits for the project are independent of + # account resource limits + # 3. Increase Projects Resources limits above domains limit. Verify + # project can’t have more resources than domain level limit allows. + # 4. Create Resource more than its set limit for a project. Verify + # resource allocation should fail giving proper message + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.admin.account.name, + domainid=self.admin.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + # Get the resource limits for ROOT domain + resource_limits = list_resource_limits(self.apiclient) + + self.assertEqual( + isinstance(resource_limits, list), + True, + "List resource API should return a valid list" + ) + self.assertNotEqual( + len(resource_limits), + 0, + "List resource API response should not be empty" + ) + + # Reduce resource limits for project + # Resource: 0 - Instance. Number of instances a user can create. + # Resource: 1 - IP. Number of public IP addresses a user can own. + # Resource: 2 - Volume. Number of disk volumes a user can create. + # Resource: 3 - Snapshot. Number of snapshots a user can create. + # Resource: 4 - Template. Number of templates that a user can + # register/create + for resource in resource_limits: + update_resource_limit( + self.apiclient, + resource.resourcetype, + max=1, + projectid=project.id + ) + self.debug( + "Updating resource (ID: %s) limit for project: %s" % ( + resource, + project.id + )) + resource_limits = list_resource_limits( + self.apiclient, + projectid=project.id + ) + self.assertEqual( + isinstance(resource_limits, list), + True, + "List resource API should return a valid list" + ) + self.assertNotEqual( + len(resource_limits), + 0, + "List resource API response should not be empty" + ) + for resource in resource_limits: + self.assertEqual( + resource.max, + 1, + "Resource limit should be updated to 1" + ) + + # Get the resource limits for domain + resource_limits = list_resource_limits( + self.apiclient, + domainid=self.domain.id + ) + self.assertEqual( + isinstance(resource_limits, list), + True, + "List resource API should return a valid list" + ) + self.assertNotEqual( + len(resource_limits), + 0, + "List resource API response should not be empty" + ) + + for resource in resource_limits: + # Update domain resource limits to 2 + update_resource_limit( + self.apiclient, + resource.resourcetype, + domainid=self.domain.id, + max=2 + ) + with self.assertRaises(Exception): + self.debug( + "Attempting to update project: %s resource limit to: %s" % ( + project.id, + max_value + )) + # Update project resource limits to 3 + update_resource_limit( + self.apiclient, + resource.resourcetype, + max=3, + projectid=project.id + ) + return + @unittest.skip("No provision for updating resource limits from account through API") + def test_02_project_limits_normal_user(self): + """ Test project limits + """ + + # Validate the following + # 1. Create a Project + # 2. Reduce the projects limits as a domain admin. Verify resource + # count is updated + # 3. Reduce the projects limits as a project user owner who is not a + # domain admin. Resource count should fail + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.admin.account.name, + domainid=self.admin.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + # Get the resource limits for ROOT domain + resource_limits = list_resource_limits(self.apiclient) + + self.assertEqual( + isinstance(resource_limits, list), + True, + "List resource API should return a valid list" + ) + self.assertNotEqual( + len(resource_limits), + 0, + "List resource API response should not be empty" + ) + + # Reduce resource limits for project + # Resource: 0 - Instance. Number of instances a user can create. + # Resource: 1 - IP. Number of public IP addresses a user can own. + # Resource: 2 - Volume. Number of disk volumes a user can create. + # Resource: 3 - Snapshot. Number of snapshots a user can create. + # Resource: 4 - Template. Number of templates that a user can + # register/create + for resource in resource_limits: + update_resource_limit( + self.apiclient, + resource.resourcetype, + max=1, + projectid=project.id + ) + self.debug( + "Updating resource (ID: %s) limit for project: %s" % ( + resource, + project.id + )) + resource_limits = list_resource_limits( + self.apiclient, + projectid=project.id + ) + self.assertEqual( + isinstance(resource_limits, list), + True, + "List resource API should return a valid list" + ) + self.assertNotEqual( + len(resource_limits), + 0, + "List resource API response should not be empty" + ) + for resource in resource_limits: + self.assertEqual( + resource.max, + 1, + "Resource limit should be updated to 1" + ) + + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + ) + + # Get the resource limits for domain + resource_limits = list_resource_limits( + self.apiclient, + domainid=self.domain.id + ) + self.assertEqual( + isinstance(resource_limits, list), + True, + "List resource API should return a valid list" + ) + self.assertNotEqual( + len(resource_limits), + 0, + "List resource API response should not be empty" + ) + + for resource in resource_limits: + #with self.assertRaises(Exception): + self.debug( + "Attempting to update resource limit by user: %s" % ( + self.user.account.name + )) + # Update project resource limits to 3 + update_resource_limit( + self.apiclient, + resource.resourcetype, + account=self.user.account.name, + domainid=self.user.account.domainid, + max=3, + projectid=project.id + ) + return + + +class TestResourceLimitsProject(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestResourceLimitsProject, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + # Create Domains, Account etc + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + # Create project as a domain admin + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls.services["account"] = cls.account.account.name + + # Create Service offering and disk offerings etc + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.disk_offering, + cls.account, + cls.domain + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_03_vm_per_project(self): + """Test VM limit per project + """ + + # Validate the following + # 1. Set max VM per project to 2 + # 2. Create account and start 2 VMs. Verify VM state is Up and Running + # 3. Try to create 3rd VM instance. The appropriate error or alert + # should be raised + + self.debug( + "Updating instance resource limits for project: %s" % + self.project.id) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 0, # Instance + max=2, + projectid=self.project.id + ) + + self.debug("Deploying VM for project: %s" % self.project.id) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + self.debug("Deploying VM for project: %s" % self.project.id) + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_2) + # Verify VM state + self.assertEqual( + virtual_machine_2.state, + 'Running', + "Check VM state is Running or not" + ) + # Exception should be raised for second instance + with self.assertRaises(Exception): + VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + return + + def test_04_publicip_per_project(self): + """Test Public IP limit per project + """ + + # Validate the following + # 1. set max no of IPs per project to 2. + # 2. Create an account in this domain + # 3. Create 1 VM in this domain + # 4. Acquire 1 IP in the domain. IP should be successfully acquired + # 5. Try to acquire 3rd IP in this domain. It should give the user an + # appropriate error and an alert should be generated. + + self.debug( + "Updating public IP resource limits for project: %s" % + self.project.id) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 1, # Public Ip + max=2, + projectid=self.project.id + ) + + self.debug("Deploying VM for Project: %s" % self.project.id) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + networks = Network.list( + self.apiclient, + projectid=self.project.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "Check list networks response returns a valid response" + ) + self.assertNotEqual( + len(networks), + 0, + "Check list networks response returns a valid network" + ) + network = networks[0] + self.debug("Associating public IP for project: %s" % + self.project.id) + public_ip_1 = PublicIPAddress.create( + self.apiclient, + zoneid=virtual_machine_1.zoneid, + services=self.services["server"], + networkid=network.id, + projectid=self.project.id + ) + self.cleanup.append(public_ip_1) + # Verify Public IP state + self.assertEqual( + public_ip_1.ipaddress.state in [ + 'Allocated', + 'Allocating' + ], + True, + "Check Public IP state is allocated or not" + ) + + # Exception should be raised for second Public IP + with self.assertRaises(Exception): + public_ip_2 = PublicIPAddress.create( + self.apiclient, + zoneid=virtual_machine_1.zoneid, + services=self.services["server"], + networkid=network.id, + projectid=self.project.id + ) + return + + def test_05_snapshots_per_project(self): + """Test Snapshot limit per project + """ + + # Validate the following + # 1. set max no of snapshots per project to 1. + # 2. Create one snapshot in the project. Snapshot should be + # successfully created + # 5. Try to create another snapshot in this project. It should give + # user an appropriate error and an alert should be generated. + + self.debug( + "Updating snapshot resource limits for project: %s" % + self.project.id) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 3, # Snapshot + max=1, + projectid=self.project.id + ) + + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + projectid=self.project.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating snapshot from volume: %s" % volumes[0].id) + # Create a snapshot from the ROOTDISK + snapshot_1 = Snapshot.create(self.apiclient, + volumes[0].id, + projectid=self.project.id + ) + self.cleanup.append(snapshot_1) + # Verify Snapshot state + self.assertEqual( + snapshot_1.state in [ + 'BackedUp', + 'CreatedOnPrimary' + ], + True, + "Check Snapshot state is Running or not" + ) + + # Exception should be raised for second snapshot + with self.assertRaises(Exception): + Snapshot.create(self.apiclient, + volumes[0].id, + projectid=self.project.id + ) + return + + def test_06_volumes_per_project(self): + """Test Volumes limit per project + """ + + # Validate the following + # 1. set max no of volume per project to 1. + # 2. Create 1 VM in this project + # 4. Try to Create another VM in the project. It should give the user + # an appropriate error that Volume limit is exhausted and an alert + # should be generated. + + self.debug( + "Updating volume resource limits for project: %s" % + self.project.id) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 2, # Volume + max=2, + projectid=self.project.id + ) + + self.debug("Deploying VM for project: %s" % self.project.id) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + # Exception should be raised for second volume + with self.assertRaises(Exception): + Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + diskofferingid=self.disk_offering.id, + projectid=self.project.id + ) + return + + def test_07_templates_per_project(self): + """Test Templates limit per project + """ + + # Validate the following + # 1. set max no of templates per project to 1. + # 2. Create a template in this project. Both template should be in + # ready state + # 3. Try create 2nd template in the project. It should give the user + # appropriate error and an alert should be generated. + + # Reset the volume limits + update_resource_limit( + self.apiclient, + 2, # Volume + max=5, + projectid=self.project.id + ) + self.debug( + "Updating template resource limits for domain: %s" % + self.account.account.domainid) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 4, # Template + max=1, + projectid=self.project.id + ) + + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + virtual_machine_1.stop(self.apiclient) + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + projectid=self.project.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating template from volume: %s" % volume.id) + # Create a template from the ROOTDISK + template_1 = Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + projectid=self.project.id + ) + + self.cleanup.append(template_1) + # Verify Template state + self.assertEqual( + template_1.isready, + True, + "Check Template is in ready state or not" + ) + + # Exception should be raised for second template + with self.assertRaises(Exception): + Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + projectid=self.project.id + ) + return diff --git a/test/integration/component/test_project_resources.py b/test/integration/component/test_project_resources.py new file mode 100644 index 00000000000..624b9846a53 --- /dev/null +++ b/test/integration/component/test_project_resources.py @@ -0,0 +1,1314 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Resource creation +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +import datetime + +class Services: + """Test Resource creation Services + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "account": { + "email": "administrator@clogeny.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "user": { + "email": "administrator@clogeny.com", + "firstname": "User", + "lastname": "User", + "username": "User", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Tiny Disk Offering", + "name": "Tiny Disk Offering", + "disksize": 1 + }, + "volume": { + "diskname": "Test Volume", + }, + "server": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "template": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "templatefilter": 'self', + "ispublic": False, + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + }, + "domain_network": { + "name": "Domainwide Network", + "displaytext": "Domainwide Network", + "gateway": '192.168.100.1', + "netmask": '255.255.255.0', + "startip": '192.168.100.200', + "endip": '192.168.100.201', + "vlan": 4001, + "acltype": 'domain' + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "protocol": "TCP" + }, + "lbrule": { + "name": "SSH", + "alg": "roundrobin", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + }, + "fw_rule": { + "startport": 1, + "endport": 6000, + "cidr": '55.55.0.0/11', + # Any network (For creating FW rule) + }, + "security_group": { + "name": 'SSH', + "protocol": 'TCP', + "startport": 22, + "endport": 22, + "cidrlist": '0.0.0.0/0', + }, + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + } + + +class TestOfferings(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestOfferings, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone and template + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + # Create domains, account etc. + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.domain.id + ) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls._cleanup = [ + cls.account, + cls.service_offering, + cls.disk_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_service_offerings(self): + """ Test service offerings in a project + """ + + # Validate the following + # 1. Create a project. + # 2. List service offerings for the project. All SO available in the + # domain can be used for project resource creation. + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + self.debug( + "Deploying VM instance for project: %s & service offering: %s" % ( + project.id, + self.service_offering.id + )) + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=project.id + ) + # Verify VM state + self.assertEqual( + virtual_machine.state, + 'Running', + "Check VM state is Running or not" + ) + + return + + def test_02_project_disk_offerings(self): + """ Test project disk offerings + """ + + # Validate the following + # 1. Create a project. + # 2. List service offerings for the project. All disk offerings + # available in the domain can be used for project resource creation + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug( + "Create a data volume for project: %s" % project.id) + # Create a volume for project + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + diskofferingid=self.disk_offering.id, + projectid=project.id + ) + self.cleanup.append(volume) + # Verify Volume state + self.assertEqual( + volume.state in [ + 'Allocated', + 'Ready' + ], + True, + "Check Volume state is Ready or not" + ) + return + + +class TestNetwork(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestNetwork, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone and template + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + # Create domains, account etc. + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.domain.id + ) + cls._cleanup = [ + cls.account, + cls.service_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_03_network_create(self): + """ Test create network in project + """ + + # Validate the following + # 1. Create a project. + # 2. Add virtual/direct network resource to the project. User shared + # network resource for the project + # 3. Verify any number of Project level Virtual/Direct networks can be + # created and used for vm deployment within the project. + # 4. Verify shared networks (zone and domain wide) from outside the + # project can also be used in a project. + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + network_offerings = list_network_offerings( + self.apiclient, + projectid=project.id, + supportedServices='SourceNat', + type='isolated', + state='Enabled' + ) + self.assertEqual( + isinstance(network_offerings, list), + True, + "Check for the valid network offerings" + ) + network_offering = network_offerings[0] + + self.debug("creating a network with network offering ID: %s" % + network_offering.id) + self.services["network"]["zoneid"] = self.zone.id + network = Network.create( + self.apiclient, + self.services["network"], + networkofferingid=network_offering.id, + projectid=project.id + ) + self.debug("Created network with ID: %s" % network.id) + networks= Network.list( + self.apiclient, + projectid=project.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "Check for the valid network list response" + ) + network_response = networks[0] + + self.debug("Deploying VM with network: %s" % network.id) + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + networkids=[str(network.id)], + serviceofferingid=self.service_offering.id, + projectid=project.id + ) + self.debug("Deployed VM with ID: %s" % virtual_machine.id) + # Verify VM state + self.assertEqual( + virtual_machine.state, + 'Running', + "Check VM state is Running or not" + ) + + network_offerings = list_network_offerings( + self.apiclient, + state='Enabled', + guestiptype='Shared', + name='DefaultSharedNetworkOffering', + displaytext='Offering for Shared networks' + ) + self.assertEqual( + isinstance(network_offerings, list), + True, + "Check for the valid network offerings" + ) + network_offering = network_offerings[0] + + self.debug("creating a shared network in domain: %s" % + self.domain.id) + domain_network = Network.create( + self.apiclient, + self.services["domain_network"], + domainid=self.domain.id, + networkofferingid=network_offering.id, + zoneid=self.zone.id + ) + self._cleanup.append(domain_network) + self.debug("Created network with ID: %s" % domain_network.id) + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + networkids=[str(domain_network.id)], + serviceofferingid=self.service_offering.id, + projectid=project.id + ) + self.debug("Deployed VM with ID: %s" % virtual_machine.id) + # Verify VM state + self.assertEqual( + virtual_machine.state, + 'Running', + "Check VM state is Running or not" + ) + return + + +class TestTemplates(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestTemplates, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + # Create Domains, Account etc + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.user = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + # Create project as a domain admin + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls.services["account"] = cls.account.account.name + + # Create Service offering and disk offerings etc + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + cls.user, + cls.domain + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_04_public_template_use_in_project(self): + """Test Templates creation in projects + """ + + # Validate the following + # 1. Create a project + # 2. Verify Public templates can be used without any restriction + # 3. Verify that template created in project can be used in project + # without any restrictions + + self.debug("Deploying VM for with public template: %s" % + self.template.id) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + virtual_machine_1.stop(self.apiclient) + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + projectid=self.project.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating template from volume: %s" % volume.id) + # Create a template from the ROOTDISK + template_1 = Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + projectid=self.project.id + ) + + self.cleanup.append(template_1) + # Verify Template state + self.assertEqual( + template_1.isready, + True, + "Check Template is in ready state or not" + ) + return + + def test_05_use_private_template_in_project(self): + """Test use of private template in a project + """ + + # Validate the following + # 1. Create a project + # 2. Verify that in order to use somebody’s Private template for vm + # creation in the project, permission to use the template has to + # be granted to the Project (use API “updateTemplatePermissions” + # with project id to achieve that). + + self.debug("Deploying VM for with public template: %s" % + self.template.id) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + self.debug("Stopping the VM: %s" % virtual_machine_1.id) + virtual_machine_1.stop(self.apiclient) + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + projectid=self.project.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating template from volume: %s" % volume.id) + # Create a template from the ROOTDISK + template_1 = Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + projectid=self.project.id + ) + + self.cleanup.append(template_1) + # Verify Template state + self.assertEqual( + template_1.isready, + True, + "Check Template is in ready state or not" + ) + + # Update template permissions to grant permission to project + self.debug( + "Updating template permissions:%s to grant access to project: %s" % ( + template_1.id, + self.project.id + )) + + template_1.updatePermissions( + self.apiclient, + op='add', + projectids=self.project.id + ) + self.debug("Deploying VM for with privileged template: %s" % + self.template.id) + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template_1.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_2) + # Verify VM state + self.assertEqual( + virtual_machine_2.state, + 'Running', + "Check VM state is Running or not" + ) + return + + +class TestSnapshots(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestSnapshots, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + # Create Domains, Account etc + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + # Create project as a domain admin + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls.services["account"] = cls.account.account.name + + # Create Service offering and disk offerings etc + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + cls.domain + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_06_create_snapshots_in_project(self): + """Test create snapshots in project + """ + + # Validate the following + # 1. Create a project + # 2. Add some snapshots to the project + # 3. Verify snapshot created inside project can only be used in inside + # the project + + self.debug("Deploying VM for Project: %s" % self.project.id) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + projectid=self.project.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating snapshot from volume: %s" % volumes[0].id) + # Create a snapshot from the ROOTDISK + snapshot = Snapshot.create(self.apiclient, + volumes[0].id, + projectid=self.project.id + ) + self.cleanup.append(snapshot) + # Verify Snapshot state + self.assertEqual( + snapshot.state in [ + 'BackedUp', + 'CreatedOnPrimary' + ], + True, + "Check Snapshot state is Running or not" + ) + + snapshots = Snapshot.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + snapshots, + None, + "Snapshots should not be available outside the project" + ) + return + + +class TestPublicIpAddress(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestPublicIpAddress, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + # Create Domains, Account etc + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + # Create project as a domain admin + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls.services["account"] = cls.account.account.name + + # Create Service offering and disk offerings etc + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=cls.template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + cls.domain + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_07_associate_public_ip(self): + """Test associate public IP within the project + """ + + # Validate the following + # 1. Create a project + # 2. Add some public Ips to the project + # 3. Verify public IP assigned can only used to create PF/LB rules + # inside project + + networks = Network.list( + self.apiclient, + projectid=self.project.id, + listall=True + ) + self.assertEqual( + isinstance(networks, list), + True, + "Check list networks response returns a valid response" + ) + self.assertNotEqual( + len(networks), + 0, + "Check list networks response returns a valid network" + ) + network = networks[0] + self.debug("Associating public IP for project: %s" % self.project.id) + public_ip = PublicIPAddress.create( + self.apiclient, + zoneid=self.virtual_machine.zoneid, + services=self.services["server"], + networkid=network.id, + projectid=self.project.id + ) + self.cleanup.append(public_ip) + + #Create NAT rule + self.debug( + "Creating a NAT rule within project, VM ID: %s" % + self.virtual_machine.id) + nat_rule = NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + public_ip.ipaddress.id, + projectid=self.project.id + ) + self.debug("created a NAT rule with ID: %s" % nat_rule.id) + nat_rule_response = NATRule.list( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(nat_rule_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(nat_rule_response), + 0, + "Check Port Forwarding Rule is created" + ) + self.assertEqual( + nat_rule_response[0].id, + nat_rule.id, + "Check Correct Port forwarding Rule is returned" + ) + + #Create Load Balancer rule and assign VMs to rule + self.debug("Created LB rule for public IP: %s" % + public_ip.ipaddress.ipaddress) + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + public_ip.ipaddress.id, + projectid=self.project.id + ) + self.cleanup.append(lb_rule) + self.debug("Assigning VM: %s to LB rule: %s" % ( + self.virtual_machine.name, + lb_rule.id + )) + lb_rule.assign(self.apiclient, [self.virtual_machine]) + + lb_rules = list_lb_rules( + self.apiclient, + id=lb_rule.id + ) + self.assertEqual( + isinstance(lb_rules, list), + True, + "Check list response returns a valid list" + ) + #verify listLoadBalancerRules lists the added load balancing rule + self.assertNotEqual( + len(lb_rules), + 0, + "Check Load Balancer Rule in its List" + ) + self.assertEqual( + lb_rules[0].id, + lb_rule.id, + "Check List Load Balancer Rules returns valid Rule" + ) + + #Create Firewall rule with configurations from settings file + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=public_ip.ipaddress.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"], + projectid=self.project.id + ) + self.debug("Created firewall rule: %s" % fw_rule.id) + + # After Router start, FW rule should be in Active state + fw_rules = FireWallRule.list( + self.apiclient, + id=fw_rule.id, + ) + self.assertEqual( + isinstance(fw_rules, list), + True, + "Check for list FW rules response return valid data" + ) + + self.assertEqual( + fw_rules[0].state, + 'Active', + "Check list load balancing rules" + ) + self.assertEqual( + fw_rules[0].startport, + str(self.services["fw_rule"]["startport"]), + "Check start port of firewall rule" + ) + + self.assertEqual( + fw_rules[0].endport, + str(self.services["fw_rule"]["endport"]), + "Check end port of firewall rule" + ) + + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + ) + self.cleanup.append(virtual_machine_1) + + self.debug("VM state after deploy: %s" % virtual_machine_1.state) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + self.debug("Creating NAT rule for VM (ID: %s) outside project" % + virtual_machine_1.id) + with self.assertRaises(Exception): + NATRule.create( + self.apiclient, + virtual_machine_1, + self.services["natrule"], + public_ip.ipaddress.id, + ) + + self.debug("Creating LB rule for public IP: %s outside project" % + public_ip.ipaddress.ipaddress) + with self.assertRaises(Exception): + LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + public_ip.ipaddress.id, + accountid=self.account.account.name + ) + self.debug( + "Creating firewall rule for public IP: %s outside project" % + public_ip.ipaddress.ipaddress) + with self.assertRaises(Exception): + FireWallRule.create( + self.apiclient, + ipaddressid=public_ip.ipaddress.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"], + ) + return + + +class TestSecurityGroup(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super( + TestSecurityGroup, + cls + ).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["server"]["zoneid"] = cls.zone.id + cls.services["server"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + # Create project as a domain admin + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls.services["account"] = cls.account.account.name + + cls._cleanup = [ + cls.project, + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestSecurityGroup, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_08_security_group(self): + """Test security groups in project + """ + + # Validate the following: + # 1. Create a project + # 2. Assign some security groups to that project + # 3. Verify the security groups can only be assigned to VM belonging + # to that project. + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + projectid=self.project.id + ) + self.debug("Created security group with ID: %s" % security_group.id) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + projectid=self.project.id + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertNotEqual( + len(sercurity_groups), + 0, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + projectid=self.project.id + ) + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + self.debug( + "Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server"], + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id], + projectid=self.project.id + ) + self.debug("Deployed VM (ID: %s) in project: %s" % ( + self.virtual_machine.id, + self.project.id + )) + self.assertEqual( + self.virtual_machine.state, + 'Running', + "VM state should be running after deployment" + ) + # Deploy another VM with same security group outside the project + self.debug( + "Deploying VM with security group: %s outside project:%s" % ( + security_group.id, + self.project.id + )) + with self.assertRaises(Exception): + VirtualMachine.create( + self.apiclient, + self.services["server"], + serviceofferingid=self.service_offering.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + securitygroupids=[security_group.id], + ) + return \ No newline at end of file diff --git a/test/integration/component/test_project_usage.py b/test/integration/component/test_project_usage.py new file mode 100644 index 00000000000..bbf6205c3a2 --- /dev/null +++ b/test/integration/component/test_project_usage.py @@ -0,0 +1,1711 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Snapshots +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +import datetime + +class Services: + """Test Snapshots Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "volume": { + "diskname": "TestDiskServ", + }, + "server": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "templates": { + "displaytext": 'Template', + "name": 'Template', + "ostypeid": '471a4b5b-5523-448f-9608-7d6218995733', + "templatefilter": 'self', + "url": "http://download.cloud.com/releases/2.0.0/UbuntuServer-10-04-64bit.qcow2.bz2" + }, + "iso": { + "displaytext": "Test ISO", + "name": "Test ISO", + "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + # Source URL where ISO is located + "isextractable": True, + "isfeatured": True, + "ispublic": True, + "ostypeid": '471a4b5b-5523-448f-9608-7d6218995733', + }, + "lbrule": { + "name": "SSH", + "alg": "roundrobin", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "protocol": "TCP" + }, + "vpn_user": { + "username": "test", + "password": "test", + }, + "ostypeid": '471a4b5b-5523-448f-9608-7d6218995733', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode":'advanced' + } + + +class TestVmUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestVmUsage, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create Account, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_vm_usage(self): + """Test Create/Destroy VM and verify usage calculation + """ + + # Validate the following + # 1. Create a VM. Verify usage_events table contains VM .create, + # VM.start , Network.offering.assign , Volume.create events + # 2. Stop the VM. Verify usage_events table contains + # network.offerings.remove ,VM .stop Events for the created account. + # 3. Destroy the VM after some time. Verify usage_events table contains + # VM.Destroy and volume .delete Event for the created account + # 4. Delete the account + + self.debug("Stopping the VM: %s" % self.virtual_machine.id) + # Stop the VM + self.virtual_machine.stop(self.apiclient) + + time.sleep(self.services["sleep"]) + # Destroy the VM + self.debug("Destroying the VM: %s" % self.virtual_machine.id) + self.virtual_machine.delete(self.apiclient) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + # Check if VM.CREATE, VM.DESTROY events present in usage_event table + self.assertEqual( + qresult.count('VM.START'), + 1, + "Check VM.START event in events table" + ) + + self.assertEqual( + qresult.count('NETWORK.OFFERING.ASSIGN'), + 1, + "Check NETWORK.OFFERING.ASSIGN in events table" + ) + self.assertEqual( + qresult.count('VM.CREATE'), + 1, + "Check VM.CREATE in list events" + ) + + self.assertEqual( + qresult.count('VOLUME.CREATE'), + 1, + "Check VOLUME.CREATE in events table" + ) + + self.assertEqual( + qresult.count('VM.STOP'), + 1, + "Check VM.STOP in events table" + ) + + self.assertEqual( + qresult.count('NETWORK.OFFERING.REMOVE'), + 1, + "Check NETWORK.OFFERING.REMOVE in list events" + ) + + self.assertEqual( + qresult.count('VM.DESTROY'), + 1, + "Check VM.DESTROY in events table" + ) + + self.assertEqual( + qresult.count('VOLUME.DELETE'), + 1, + "Check VOLUME.DELETE in events table" + ) + return + + +class TestPublicIPUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestPublicIPUsage, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = cls.template.id + + # Create VMs, Assign Public IP etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=cls.template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + networks = Network.list( + cls.api_client, + projectid=cls.project.id, + listall=True + ) + if isinstance(networks, list): + network = networks[0] + else: + raise Exception("List networks call failed") + + cls.public_ip = PublicIPAddress.create( + cls.api_client, + zoneid=cls.zone.zoneid, + services=cls.services["server"], + networkid=network.id, + projectid=cls.project.id + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_public_ip_usage(self): + """Test Assign new IP and verify usage calculation + """ + + # Validate the following + # 1. Acquire a IP for the network of this account. Verify usage_event + # table has Acquire IP event for the IP for this account + # 2. Release one of the IP of this account. Verify usage_event table + # has IP.Release event for released IP for this account + # 3. Delete the newly created account + + self.debug("Deleting public IP: %s" % + self.public_ip.ipaddress.ipaddress) + + # Release one of the IP + self.public_ip.delete(self.apiclient) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + # Check if NET.IPASSIGN, NET.IPRELEASE events present in usage_event + # table + self.assertEqual( + qresult.count('NET.IPASSIGN') > 0, + True, + "Check NET.IPASSIGN event in events table" + ) + + self.assertEqual( + qresult.count('NET.IPRELEASE') > 0, + True, + "Check NET.IPRELEASE in events table" + ) + return + + +class TestVolumeUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestVolumeUsage, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + cls.services["server"]["diskoffering"] = cls.disk_offering.id + cls.services["template"] = template.id + + # Create Account, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.disk_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_volume_usage(self): + """Test Create/delete a volume and verify correct usage is recorded + """ + + # Validate the following + # 1. Volume.create event for both root and data disk is there for the + # created account in cloud.usage_event table + # 2. Stop the VM + # 3. Detach the data disk from this VM + # 4. Destroy the Data disk. Volume.delete event is generated for data + # disk of the destroyed VM + + # Stop VM + self.debug("Stopping VM with ID: %s" % self.virtual_machine.id) + self.virtual_machine.stop(self.apiclient) + + volume_response = list_volumes( + self.apiclient, + projectid=self.project.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(volume_response, list), + True, + "Check for valid list volumes response" + ) + data_volume = volume_response[0] + + # Detach data Disk + self.debug("Detaching volume ID: %s VM with ID: %s" % ( + data_volume.id, + self.virtual_machine.id + )) + self.virtual_machine.detach_volume(self.apiclient, data_volume) + + # Delete Data disk + self.debug("Delete volume ID: %s" % data_volume.id) + cmd = deleteVolume.deleteVolumeCmd() + cmd.id = data_volume.id + self.apiclient.deleteVolume(cmd) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + # Check VOLUME.CREATE, VOLUME.DESTROY events in cloud.usage_event table + self.assertEqual( + qresult.count('VOLUME.CREATE'), + 2, + "Check VOLUME.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('VOLUME.DELETE'), + 1, + "Check VOLUME.DELETE in events table" + ) + return + + +class TestTemplateUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestTemplateUsage, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["server"]["zoneid"] = cls.zone.id + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + #create virtual machine + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + + #Stop virtual machine + cls.virtual_machine.stop(cls.api_client) + + #Wait before server has be successfully stopped + time.sleep(30) + list_volume = list_volumes( + cls.api_client, + projectid=cls.project.id, + type='ROOT', + listall=True + ) + if isinstance(list_volume, list): + cls.volume = list_volume[0] + else: + raise Exception("List Volumes failed!") + cls._cleanup = [ + cls.project, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, templates + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_template_usage(self): + """Test Upload/ delete a template and verify correct usage is generated + for the template uploaded + """ + + # Validate the following + # 1. Create a account + # 2. Upload a template from this account. template.create event is + # recorded in cloud.usage_event table for this account + # 3. Delete the template. template.delete event is recorded in + # cloud.usage_event tables for this account + # 4. Destroy the account + + #Create template from Virtual machine and Volume ID + self.template = Template.create( + self.apiclient, + self.services["templates"], + self.volume.id, + projectid=self.project.id + ) + self.debug("Created template with ID: %s" % self.template.id) + # Delete template + self.template.delete(self.apiclient) + self.debug("Deleted template with ID: %s" % self.template.id) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for TEMPLATE.CREATE, TEMPLATE.DELETE in cloud.usage_event table + self.assertEqual( + qresult.count('TEMPLATE.CREATE'), + 1, + "Check TEMPLATE.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('TEMPLATE.DELETE'), + 1, + "Check TEMPLATE.DELETE in events table" + ) + return + + +class TestISOUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestISOUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["server"]["zoneid"] = cls.zone.id + cls.services["iso"]["zoneid"] = cls.zone.id + # Create Account, ISO image etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.iso = Iso.create( + cls.api_client, + cls.services["iso"], + projectid=cls.project.id + ) + try: + # Wait till ISO gets downloaded + cls.iso.download(cls.api_client) + except Exception as e: + raise Exception("%s: Failed to download ISO: %s" % ( + e, + cls.iso.id + )) + cls._cleanup = [ + cls.project, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created ISO images + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_ISO_usage(self): + """Test Create/Delete a ISO and verify its usage is generated correctly + """ + + # Validate the following + # 1. Create a account + # 2. Upload a ISO from this account. ISO.create event is recorded in + # cloud.usage_event table for this account + # 3. Delete the ISO. ISO.delete event is recorded in cloud.usage_event + # tables for this account + # 4. Destroy the account + + # Delete the ISO + self.debug("Deleting ISO with ID: %s" % self.iso.id) + self.iso.delete(self.apiclient) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for ISO.CREATE, ISO.DELETE events in cloud.usage_event table + self.assertEqual( + qresult.count('ISO.CREATE'), + 1, + "Check ISO.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('ISO.DELETE'), + 1, + "Check ISO.DELETE in events table" + ) + return + + +class TestLBRuleUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestLBRuleUsage, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create VMs, LB Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + networks = Network.list( + cls.api_client, + projectid=cls.project.id, + listall=True + ) + if isinstance(networks, list): + network = networks[0] + else: + raise Exception("List networks call failed") + + cls.public_ip_1 = PublicIPAddress.create( + cls.api_client, + zoneid=cls.zone.zoneid, + services=cls.services["server"], + networkid=network.id, + projectid=cls.project.id + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, LB rules + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_lb_usage(self): + """Test Create/Delete a LB rule and verify correct usage is recorded + """ + + # Validate the following + # 1. Acquire a IP for this account. lb.rule.create event is registered + # for this account in cloud.usage_event table + # 2. Create a LB rule on the IP associated with this account + # 3. Delete the created LB rule from the account. lb.rule.delete event + # is registered for this account in cloud.usage_event table + # 4. Delete this account. + + self.debug( + "Creating load balancer rule for public IP: %s" % + self.public_ip_1.ipaddress.id) + #Create Load Balancer rule and assign VMs to rule + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + self.public_ip_1.ipaddress.id, + projectid=self.project.id + ) + # Delete LB Rule + self.debug("Deleting LB rule with ID: %s" % lb_rule.id) + lb_rule.delete(self.apiclient) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for LB.CREATE, LB.DELETE in cloud.usage_event table + self.assertEqual( + qresult.count('LB.CREATE'), + 1, + "Check LB.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('LB.DELETE'), + 1, + "Check LB.DELETE in events table" + ) + return + + +class TestSnapshotUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestSnapshotUsage, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create Account, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_snapshot_usage(self): + """Test Create/Delete a manual snap shot and verify + correct usage is recorded + """ + + # Validate the following + # 1. Create snapshot of the root disk for this account.Snapshot.create + # event is there for the created account in cloud.usage_event table + # 2. Destroy the snapshot after some time. Snapshot.delete event is + # generated for the destroyed Snapshot + # 3. Delete the account + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + projectid=self.project.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check if list volumes return a valid data" + ) + + volume = volumes[0] + + # Create a snapshot from the ROOTDISK + self.debug("Creating snapshot from volume: %s" % volumes[0].id) + snapshot = Snapshot.create(self.apiclient, volumes[0].id) + + # Delete snapshot Rule + self.debug("Deleting snapshot: %s" % snapshot.id) + snapshot.delete(self.apiclient) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check if database query returns a valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query Result: %s" % qresult) + + # Check for SNAPSHOT.CREATE, SNAPSHOT.DELETE events in cloud.usage_event + # table + self.assertEqual( + qresult.count('SNAPSHOT.CREATE'), + 1, + "Check SNAPSHOT.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('SNAPSHOT.DELETE'), + 1, + "Check SNAPSHOT.DELETE in events table" + ) + return + + +class TestNatRuleUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestNatRuleUsage, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + networks = Network.list( + cls.api_client, + projectid=cls.project.id, + listall=True + ) + if isinstance(networks, list): + network = networks[0] + else: + raise Exception("List networks call failed") + + cls.public_ip_1 = PublicIPAddress.create( + cls.api_client, + zoneid=cls.zone.zoneid, + services=cls.services["server"], + networkid=network.id, + projectid=cls.project.id + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, NAT rules + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_nat_usage(self): + """Test Create/Delete a PF rule and verify correct usage is recorded + """ + + # Validate the following + # 1. Acquire a IP for this account + # 2. Create a PF rule on the IP associated with this account. + # NET.RULEADD event is registered for this account in + # cloud.usage_event table + # 3. Delete the created PF rule from the account. NET.RULEDelete event + # is registered for this account in cloud.usage_event table + # 4. Delete this account. + + self.debug("Creating NAT rule with public IP: %s" % + self.public_ip_1.ipaddress.id) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + self.public_ip_1.ipaddress.id + ) + + # Delete NAT Rule + self.debug("Deleting NAT rule: %s" % nat_rule.id) + nat_rule.delete(self.apiclient) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for NET.RULEADD, NET.RULEDELETE in cloud.usage_event table + self.assertEqual( + qresult.count('NET.RULEADD'), + 1, + "Check NET.RULEADD event in events table" + ) + + self.assertEqual( + qresult.count('NET.RULEDELETE'), + 1, + "Check NET.RULEDELETE in events table" + ) + return + + +class TestVpnUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestVpnUsage, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create Service offerings, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + serviceofferingid=cls.service_offering.id, + projectid=cls.project.id + ) + networks = Network.list( + cls.api_client, + projectid=cls.project.id, + listall=True + ) + if isinstance(networks, list): + network = networks[0] + else: + raise Exception("List networks call failed") + + cls.public_ip = PublicIPAddress.create( + cls.api_client, + zoneid=cls.zone.zoneid, + services=cls.services["server"], + networkid=network.id, + projectid=cls.project.id + ) + cls._cleanup = [ + cls.project, + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, VPN users + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_vpn_usage(self): + """Test Create/Delete a VPN and verify correct usage is recorded + """ + + # Validate the following + # 1. Enable VPN for this IP. vpn.add.user event is registered for this + # account in cloud.usage_event table + # 2. Add user to this vpn + # 3. Delete user for this VPN. vpn.user.delete event is registered for + # this account in cloud.usage_event table + # 4. Delete this account. + + self.debug("Created VPN with public IP: %s" % + self.public_ip.ipaddress.id) + #Assign VPN to Public IP + vpn = Vpn.create( + self.apiclient, + self.public_ip.ipaddress.id, + projectid=self.project.id + ) + + self.debug("Created VPN user for account: %s" % + self.account.account.name) + + vpnuser = VpnUser.create( + self.apiclient, + self.services["vpn_user"]["username"], + self.services["vpn_user"]["password"], + projectid=self.project.id + ) + + # Remove VPN user + self.debug("Deleting VPN user: %s" % vpnuser.id) + vpnuser.delete(self.apiclient) + + # Delete VPN access + self.debug("Deleting VPN: %s" % vpn.publicipid) + vpn.delete(self.apiclient) + + # Fetch project account ID from project UUID + self.debug( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id) + + qresultset = self.dbclient.execute( + "select project_account_id from projects where uuid = '%s';" \ + % self.project.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for VPN user related events + self.assertEqual( + qresult.count('VPN.USER.ADD'), + 1, + "Check VPN.USER.ADD event in events table" + ) + + self.assertEqual( + qresult.count('VPN.USER.ADD'), + 1, + "Check VPN.USER.ADD in events table" + ) + return diff --git a/test/integration/component/test_projects.py b/test/integration/component/test_projects.py new file mode 100644 index 00000000000..8dd64ee8a46 --- /dev/null +++ b/test/integration/component/test_projects.py @@ -0,0 +1,1809 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Project +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +import datetime + + +class Services: + """Test Project Services + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "project": { + "name": "Project", + "displaytext": "Test project", + }, + "mgmt_server": { + "ipaddress": '192.168.100.21', + "username": 'root', + "password": 'fr3sca', + "port": 22, + }, + "account": { + "email": "administrator@clogeny.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "user": { + "email": "administrator@clogeny.com", + "firstname": "User", + "lastname": "User", + "username": "User", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "disk_offering": { + "displaytext": "Tiny Disk Offering", + "name": "Tiny Disk Offering", + "disksize": 1 + }, + "volume": { + "diskname": "Test Volume", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "virtual_machine": { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "ostypeid": '8531d1df-faac-4895-a741-238d3b10e6e6', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode":'advanced' + } + + +class TestMultipleProjectCreation(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestMultipleProjectCreation, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.zone = get_zone(cls.api_client, cls.services) + + # Create domains, account etc. + cls.domain = get_domain( + cls.api_client, + cls.services + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.user = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls._cleanup = [cls.account, cls.user] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_create_multiple_projects_by_account(self): + """ Verify an account can own multiple projects and can belong to + multiple projects + """ + + # Validate the following + # 1. Create multiple project. Verify at step 1 An account is allowed + # to create multiple projects + # 2. add one account to multiple project. Verify at step 2 an account + # is allowed to added to multiple project + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Create project as a domain admin + project_1 = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project_1) + self.debug("Created project with domain admin with ID: %s" % + project_1.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project_1.id, + listall=True + ) + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project_1.name, + list_project.name, + "Check project name from list response" + ) + # Create another project as a domain admin + project_2 = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project_2) + self.debug("Created project with domain user with ID: %s" % + project_2.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project_2.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + # Add user to the project + project_1.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project_1.id, + account=self.user.account.name, + ) + self.debug(accounts_reponse) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + # Add user to the project + project_2.addAccount( + self.apiclient, + self.user.account.name, + self.user.account.email + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project_2.id, + account=self.user.account.name, + ) + self.debug(accounts_reponse) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + return + + +class TestCrossDomainAccountAdd(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestCrossDomainAccountAdd, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.zone = get_zone(cls.api_client, cls.services) + cls.domain = get_domain( + cls.api_client, + cls.services + ) + + # Create domains, account etc. + cls.new_domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.user = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.new_domain.id + ) + + cls._cleanup = [cls.account, cls.user] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_02_cross_domain_account_add(self): + """ Verify No cross domain projects + """ + + # Validate the following + # 1. Create a project in a domain. + # 2. Add different domain account to the project. Add account should + # fail + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + + self.debug("Adding user: %s from domain: %s to project: %s" %( + self.user.account.name, + self.user.account.domainid, + project.id + )) + with self.assertRaises(Exception): + # Add user to the project from different domain + project.addAccount( + self.apiclient, + self.user.account.name + ) + self.debug("User add to project failed!") + return + + +class TestDeleteAccountWithProject(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestDeleteAccountWithProject, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.zone = get_zone(cls.api_client, cls.services) + cls.domain = get_domain( + cls.api_client, + cls.services + ) + + # Create account + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup = [cls.account] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_03_delete_account_with_project(self): + """ Test As long as the project exists, its owner can't be removed + """ + + # Validate the following + # 1. Create a project. + # 2. Delete account who is owner of the project. Delete account should + # fail + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + # Deleting account who is owner of the project + with self.assertRaises(Exception): + self.account.delete(self.apiclient) + self.debug("Deleting account %s failed!" % + self.account.account.name) + return + +@unittest.skip("Deleting domain doesn't cleanup account") +class TestDeleteDomainWithProject(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestDeleteDomainWithProject, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.zone = get_zone(cls.api_client, cls.services) + # Create account + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_04_delete_domain_with_project(self): + """ Test Verify delete domain with cleanup=true should delete projects + belonging to the domain + """ + + # Validate the following + # 1. Create a project in a domain + # 2. Delete domain forcefully. Verify that project is also deleted as + # as part of domain cleanup + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + + self.debug("Deleting domain: %s forcefully" % self.domain.name) + # Delete domain with cleanup=True + self.domain.delete(self.apiclient, cleanup=True) + self.debug("Removed domain: %s" % self.domain.name) + + interval = list_configurations( + self.apiclient, + name='account.cleanup.interval' + ) + self.assertEqual( + isinstance(interval, list), + True, + "Check if account.cleanup.interval config present" + ) + self.debug( + "Sleep for account cleanup interval: %s" % + interval[0].value) + # Sleep to ensure that all resources are deleted + time.sleep(int(interval[0].value)) + + # Project should be deleted as part of domain cleanup + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + self.assertEqual( + list_projects_reponse, + None, + "Project should be deleted as part of domain cleanup" + ) + return + + +class TestProjectOwners(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestProjectOwners, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.domain = get_domain( + cls.api_client, + cls.services + ) + cls.zone = get_zone(cls.api_client, cls.services) + + # Create accounts + cls.admin = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.new_admin = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup = [cls.admin, cls.new_admin] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_05_user_project_owner_promotion(self): + """ Test Verify a project user can be later promoted to become a + owner + """ + + # Validate the following + # 1. Create a project. + # 2. Add account to the project. Edit account to make it a project + # owner. verify new user is project owner and old account is + # regular user of the project. + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.admin.account.name, + domainid=self.admin.account.domainid + ) + self.cleanup.append(project) + # Cleanup created project at end of test + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.new_admin.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.new_admin.account.name, + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.new_admin.account.name, + ) + self.debug(accounts_reponse) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + + # Update the project with new admin + project.update( + self.apiclient, + account=self.new_admin.account.name + ) + + # listProjectAccount to verify the user is new admin of the project + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.new_admin.account.name, + ) + self.debug(accounts_reponse) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Admin', + "Newly added user is not added as a regular user" + ) + + # listProjectAccount to verify old user becomes a regular user + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.admin.account.name, + ) + self.debug(accounts_reponse) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + return + + def test_06_max_one_project_owner(self): + """ Test Verify there can only be one owner of a project at a time + """ + + # Validate the following + # 1. Create a project. + # 2. Add account to the project. Edit account to make it a project + # owner. + # 3. Update project to add another account as an owner + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.admin.account.name, + domainid=self.admin.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + self.user = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup.append(self.user) + self.debug("Created account with ID: %s" % + self.user.account.name) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.new_admin.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.new_admin.account.name, + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.new_admin.account.name, + ) + self.debug(accounts_reponse) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + self.debug("Updating project with new Admin: %s" % + self.new_admin.account.name) + # Update the project with new admin + project.update( + self.apiclient, + account=self.new_admin.account.name + ) + + # listProjectAccount to verify the user is new admin of the project + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.new_admin.account.name, + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Admin', + "Newly added user is not added as a regular user" + ) + + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.user.account.name, + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + + self.debug("Updating project with new Admin: %s" % + self.user.account.name) + + # Update the project with new admin + project.update( + self.apiclient, + account=self.user.account.name + ) + + # listProjectAccount to verify the user is new admin of the project + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.user.account.name, + ) + self.debug(accounts_reponse) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Admin', + "Newly added user is not added as a regular user" + ) + + # listProjectAccount to verify old user becomes a regular user + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.new_admin.account.name, + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + return + + +class TestProjectResources(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestProjectResources, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone + cls.zone = get_zone(cls.api_client, cls.services) + cls.domain = get_domain( + cls.api_client, + cls.services + ) + + # Create account, disk offering etc. + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.user = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup = [cls.account, cls.disk_offering] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_07_project_resources_account_delete(self): + """ Test Verify after an account is removed from the project, all his + resources stay with the project. + """ + + # Validate the following + # 1. Create a project. + # 2. Add some accounts to project. Add resources to the project + # 3. Delete the account. Verify resources are still there after + # account deletion. + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.cleanup.append(project) + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name, + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.user.account.name, + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + # Create some resources(volumes) for the projects + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + diskofferingid=self.disk_offering.id, + projectid=project.id + ) + self.cleanup.append(volume) + + # Delete the project user + self.user.delete(self.apiclient) + + volumes = Volume.list(self.apiclient, id=volume.id) + + self.assertEqual( + isinstance(volumes, list), + True, + "Check for a valid list volumes response" + ) + + self.assertNotEqual( + len(volumes), + 0, + "Check list volumes API response returns a valid list" + ) + volume_response = volumes[0] + + self.assertEqual( + volume_response.name, + volume.name, + "Volume should exist after project user deletion." + ) + return + + def test_08_cleanup_after_project_delete(self): + """ Test accounts are unassigned from project after project deletion + """ + + # Validate the following + # 1. Create a project. + # 2. Add some accounts to project. Add resources to the project + # 3. Delete the project. Verify resources are freed after + # account deletion. + # 4. Verify all accounts are unassigned from project. + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Create project as a domain admin + project = Project.create( + self.apiclient, + self.services["project"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Cleanup created project at end of test + self.debug("Created project with domain admin with ID: %s" % + project.id) + + list_projects_reponse = Project.list( + self.apiclient, + id=project.id, + listall=True + ) + + self.assertEqual( + isinstance(list_projects_reponse, list), + True, + "Check for a valid list projects response" + ) + list_project = list_projects_reponse[0] + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + + self.assertEqual( + project.name, + list_project.name, + "Check project name from list response" + ) + self.user = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.cleanup.append(self.user) + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + project.name + )) + # Add user to the project + project.addAccount( + self.apiclient, + self.user.account.name + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=project.id, + account=self.user.account.name, + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(list_projects_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + # Create some resources(volumes) for the projects + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + diskofferingid=self.disk_offering.id, + projectid=project.id + ) + self.debug("Created a volume: %s for project: %s" % ( + volume.id, + project.name + )) + # Delete the project user + self.debug("Deleting project: %s" % project.name) + project.delete(self.apiclient) + self.debug("Successfully deleted project: %s" % project.name) + + volumes = Volume.list(self.apiclient, id=volume.id) + + self.assertEqual( + volumes, + None, + "Resources (volume) should be deleted as part of cleanup" + ) + + accounts = Project.listAccounts(self.apiclient, projectid=project.id) + + self.assertEqual( + accounts, + None, + "Accounts should be un-assigned from project" + ) + return + + +class TestProjectSuspendActivate(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestProjectSuspendActivate, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, domain, template etc + cls.zone = get_zone(cls.api_client, cls.services) + cls.domain = get_domain( + cls.api_client, + cls.services + ) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + # Create account, service offering, disk offering etc. + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"], + domainid=cls.domain.id + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.user = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + # Create project as a domain admin + cls.project = Project.create( + cls.api_client, + cls.services["project"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls._cleanup = [ + cls.project, + cls.account, + cls.disk_offering, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created accounts, domains etc + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_09_project_suspend(self): + """ Test Verify after an account is removed from the project, all his + resources stay with the project. + """ + + # Validate the following + # 1. Create a project. + # 2. Add some accounts to project. Add resources to the project + # 3. Delete the account. Verify resources are still there after + # account deletion. + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + self.debug("Adding %s user to project: %s" % ( + self.user.account.name, + self.project.name + )) + # Add user to the project + self.project.addAccount( + self.apiclient, + self.user.account.name, + ) + + # listProjectAccount to verify the user is added to project or not + accounts_reponse = Project.listAccounts( + self.apiclient, + projectid=self.project.id, + account=self.user.account.name, + ) + self.assertEqual( + isinstance(accounts_reponse, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(accounts_reponse), + 0, + "Check list project response returns a valid project" + ) + account = accounts_reponse[0] + + self.assertEqual( + account.role, + 'Regular', + "Newly added user is not added as a regular user" + ) + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + self.debug("Created a VM: %s for project: %s" % ( + virtual_machine.id, + self.project.id + )) + self.debug("Suspending a project: %s" % self.project.name) + self.project.suspend(self.apiclient) + + # Check status of all VMs associated with project + vms = VirtualMachine.list( + self.apiclient, + projectid=self.project.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(vms), + 0, + "Check list project response returns a valid project" + ) + + for vm in vms: + self.debug("VM ID: %s state: %s" % (vm.id, vm.state)) + self.assertEqual( + vm.state, + 'Stopped', + "VM should be in stopped state after project suspension" + ) + + self.debug("Attempting to create volume in suspended project") + with self.assertRaises(Exception): + # Create some resources(volumes) for the projects + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + diskofferingid=self.disk_offering.id, + projectid=self.project.id + ) + + self.debug("Volume creation failed") + + # Start the stopped VM + self.debug("Attempting to start VM: %s in suspended project" % + virtual_machine.id) + with self.assertRaises(Exception): + virtual_machine.start(self.apiclient) + self.debug("VM start failed!") + + # Destroy Stopped VM + virtual_machine.delete(self.apiclient) + self.debug("Destroying VM: %s" % virtual_machine.id) + + # Check status of all VMs associated with project + vms = VirtualMachine.list( + self.apiclient, + projectid=self.project.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(vms), + 0, + "Check list project response returns a valid project" + ) + + for vm in vms: + self.debug("VM ID: %s state: %s" % (vm.id, vm.state)) + self.assertEqual( + vm.state, + 'Destroyed', + "VM should be in stopped state after project suspension" + ) + return + + def test_10_project_activation(self): + """ Test project activation after suspension + """ + + # Validate the following + # 1. Activate the project + # 2. Verify project is activated and we are able to add resources + + # Verify 'project.invite.required' is set to false + configs = Configurations.list( + self.apiclient, + name='project.invite.required' + ) + self.assertEqual( + isinstance(configs, list), + True, + "Check for a valid list configurations response" + ) + config = configs[0] + self.assertEqual( + (config.value).lower(), + 'false', + "'project.invite.required' should be set to false" + ) + + # Activating the project + self.debug("Activating project: %s" % self.project.name) + self.project.activate(self.apiclient) + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + serviceofferingid=self.service_offering.id, + projectid=self.project.id + ) + + self.cleanup.append(virtual_machine) + self.debug("Created a VM: %s for project: %s" % ( + virtual_machine.id, + self.project.id + )) + # Check status of all VMs associated with project + vms = VirtualMachine.list( + self.apiclient, + id=virtual_machine.id, + listall=True + ) + self.assertEqual( + isinstance(vms, list), + True, + "Check for a valid list accounts response" + ) + + self.assertNotEqual( + len(vms), + 0, + "Check list project response returns a valid project" + ) + + for vm in vms: + self.debug("VM ID: %s state: %s" % (vm.id, vm.state)) + self.assertEqual( + vm.state, + 'Running', + "VM should be in Running state after project activation" + ) + return \ No newline at end of file diff --git a/test/integration/component/test_resource_limits.py b/test/integration/component/test_resource_limits.py new file mode 100644 index 00000000000..9dea37f2ac0 --- /dev/null +++ b/test/integration/component/test_resource_limits.py @@ -0,0 +1,1472 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Resource limits +""" +#Import Local Modules +import marvin +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +import datetime + +class Services: + """Test Resource Limits Services + """ + + def __init__(self): + self.services = { + "domain": { + "name": "Domain", + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "volume": { + "diskname": "TestDiskServ", + }, + "server": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "template": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + "templatefilter": 'self', + }, + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + } + +class TestResourceLimitsAccount(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestResourceLimitsAccount, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + # Create Account, VMs etc + cls.account_1 = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + # Create Account, VMs etc + cls.account_2 = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls._cleanup = [ + cls.disk_offering, + cls.service_offering, + cls.account_1, + cls.account_2 + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_vm_per_account(self): + """Test VM limit per account + """ + + # Validate the following + # 1. Set user_vm=1 limit for account 1. + # 2. Try to start 2 VMs account 1. Verify start of second VM is denied + # for this account. + # 3. Try to start 2 VMs account 2. Verify 2 SM are started properly + + self.debug( + "Updating instance resource limit for account: %s" % + self.account_1.account.name) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 0, # Instance + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + max=1 + ) + self.debug( + "Deploying VM instance in account: %s" % + self.account_1.account.name) + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_1.account.name, + domainid=self.account_1.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine) + + # Verify VM state + self.assertEqual( + virtual_machine.state, + 'Running', + "Check VM state is Running or not" + ) + + # Exception should be raised for second instance (account_1) + with self.assertRaises(Exception): + VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_1.account.name, + domainid=self.account_1.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug( + "Deploying VM instance in account: %s" % + self.account_2.account.name) + # Start 2 instances for account_2 + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_2.account.name, + domainid=self.account_2.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + self.debug( + "Deploying VM instance in account: %s" % + self.account_2.account.name) + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_2.account.name, + domainid=self.account_2.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_2) + # Verify VM state + self.assertEqual( + virtual_machine_2.state, + 'Running', + "Check VM state is Running or not" + ) + return + + def test_02_publicip_per_account(self): + """Test Public IP limit per account + """ + + # Validate the following + # 1. Set Public_IP= 2 limit for account 1. + # 2. start 1 VMs account 1 + # 3. start 1 VMs account 2 + # 4. Acquire 2 IP in account 1. Verify account with limit should be + # denied to acquire more than one IP. + # 5. Acquire 2 IP in account 2. Verify account 2 should be able to + # Acquire IP without any warning + + self.debug( + "Updating public IP resource limit for account: %s" % + self.account_1.account.name) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 1, # Public Ip + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + max=2 + ) + + self.debug( + "Deploying VM instance in account: %s" % + self.account_1.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_1.account.name, + domainid=self.account_1.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + self.debug( + "Deploying VM instance in account: %s" % + self.account_2.account.name) + # Create VM for second account + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_2.account.name, + domainid=self.account_2.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_2) + # Verify VM state + self.assertEqual( + virtual_machine_2.state, + 'Running', + "Check VM state is Running or not" + ) + self.debug( + "Associating public IP for account: %s" % + virtual_machine_1.account) + public_ip_1 = PublicIPAddress.create( + self.apiclient, + virtual_machine_1.account, + virtual_machine_1.zoneid, + virtual_machine_1.domainid, + self.services["server"] + ) + self.cleanup.append(public_ip_1) + + # Sleep to ensure that state is reflected across all the calls + time.sleep(self.services["sleep"]) + + # Verify Public IP state + self.assertEqual( + public_ip_1.ipaddress.state in [ + 'Allocated', + 'Allocating' + ], + True, + "Check Public IP state is allocated or not" + ) + + # Exception should be raised for second instance (account_1) + with self.assertRaises(Exception): + public_ip_2 = PublicIPAddress.create( + self.apiclient, + virtual_machine_1.account, + virtual_machine_1.zoneid, + virtual_machine_1.domainid, + self.services["server"] + ) + + self.debug( + "Associating public IP for account: %s" % + virtual_machine_2.account) + # Assign Public IP for account 2 + public_ip_3 = PublicIPAddress.create( + self.apiclient, + virtual_machine_2.account, + virtual_machine_2.zoneid, + virtual_machine_2.domainid, + self.services["server"] + ) + self.cleanup.append(public_ip_3) + + # Verify Public IP state + self.assertEqual( + public_ip_3.ipaddress.state in [ + 'Allocated', + 'Allocating' + ], + True, + "Check Public IP state is allocated or not" + ) + self.debug( + "Associating public IP for account: %s" % + virtual_machine_2.account) + public_ip_4 = PublicIPAddress.create( + self.apiclient, + virtual_machine_2.account, + virtual_machine_2.zoneid, + virtual_machine_2.domainid, + self.services["server"] + ) + self.cleanup.append(public_ip_4) + # Verify Public IP state + self.assertEqual( + public_ip_4.ipaddress.state in [ + 'Allocated', + 'Allocating' + ], + True, + "Check Public IP state is allocated or not" + ) + return + + def test_03_snapshots_per_account(self): + """Test Snapshot limit per account + """ + + # Validate the following + # 1. Set snapshot= 2 limit for account 1. + # 2. start 1 VMs account 1 + # 3. start 1 VMs account 2 + # 4. Create 2 snapshot in account 1. Verify account with limit should + # be denied to create more than one snapshot. + # 5. Create 2 snapshot in account 2. Verify account 2 should be able to + # create snapshots without any warning + + self.debug( + "Updating public IP resource limit for account: %s" % + self.account_1.account.name) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 3, # Snapshot + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + max=1 + ) + + self.debug( + "Deploying VM instance in account: %s" % + self.account_1.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_1.account.name, + domainid=self.account_1.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + self.debug( + "Deploying VM instance in account: %s" % + self.account_1.account.name) + # Create VM for second account + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_2.account.name, + domainid=self.account_2.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_2) + # Verify VM state + self.assertEqual( + virtual_machine_2.state, + 'Running', + "Check VM state is Running or not" + ) + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=virtual_machine_1.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating snapshot from volume: %s" % volumes[0].id) + # Create a snapshot from the ROOTDISK (Account 1) + snapshot_1 = Snapshot.create(self.apiclient, + volumes[0].id, + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + ) + self.cleanup.append(snapshot_1) + # Verify Snapshot state + self.assertEqual( + snapshot_1.state in [ + 'BackedUp', + 'CreatedOnPrimary' + ], + True, + "Check Snapshot state is Running or not" + ) + + # Exception should be raised for second snapshot (account_1) + with self.assertRaises(Exception): + Snapshot.create(self.apiclient, + volumes[0].id, + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + ) + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=virtual_machine_2.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating snapshot from volume: %s" % volumes[0].id) + # Create a snapshot from the ROOTDISK (Account 2) + snapshot_2 = Snapshot.create(self.apiclient, + volumes[0].id, + account=self.account_2.account.name, + domainid=self.account_2.account.domainid, + ) + self.cleanup.append(snapshot_2) + # Verify Snapshot state + self.assertEqual( + snapshot_2.state in [ + 'BackedUp', + 'CreatedOnPrimary' + ], + True, + "Check Snapshot state is Running or not" + ) + + self.debug("Creating snapshot from volume: %s" % volumes[0].id) + # Create a second snapshot from the ROOTDISK (Account 2) + snapshot_3 = Snapshot.create(self.apiclient, + volumes[0].id, + account=self.account_2.account.name, + domainid=self.account_2.account.domainid, + ) + self.cleanup.append(snapshot_3) + # Verify Snapshot state + self.assertEqual( + snapshot_3.state in [ + 'BackedUp', + 'CreatedOnPrimary' + ], + True, + "Check Snapshot state is Running or not" + ) + return + + def test_04_volumes_per_account(self): + """Test Volumes limit per account + """ + + # Validate the following + # 1. Set volumes=2 limit for account 1. + # 2. Start 1 VMs account 1 + # 3. Start 1 VMs account 2 + # 4. Create 2 volumes in account 1. Verify account with limit should be + # denied to create more than one volume. + # 5. Create 2 volumes in account 2. Verify account 2 should be able to + # create Volume without any warning + + self.debug( + "Updating volume resource limit for account: %s" % + self.account_1.account.name) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 2, # Volume + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + max=2 + ) + + self.debug( + "Deploying VM for account: %s" % self.account_1.account.name) + + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_1.account.name, + domainid=self.account_1.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + self.debug( + "Deploying VM for account: %s" % self.account_2.account.name) + + # Create VM for second account + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_2.account.name, + domainid=self.account_2.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_2) + # Verify VM state + self.assertEqual( + virtual_machine_2.state, + 'Running', + "Check VM state is Running or not" + ) + + self.debug( + "Create a data volume for account: %s" % self.account_1.account.name) + volume_1 = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.cleanup.append(volume_1) + # Verify Volume state + self.assertEqual( + volume_1.state in [ + 'Allocated', + 'Ready' + ], + True, + "Check Volume state is Ready or not" + ) + + # Exception should be raised for second snapshot (account_1) + with self.assertRaises(Exception): + Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + diskofferingid=self.disk_offering.id + ) + + self.debug( + "Create a data volume for account: %s" % self.account_2.account.name) + # Create volume for Account 2 + volume_2 = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account_2.account.name, + domainid=self.account_2.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.cleanup.append(volume_2) + # Verify Volume state + self.assertEqual( + volume_2.state in [ + 'Allocated', + 'Ready' + ], + True, + "Check Volume state is Ready or not" + ) + + self.debug( + "Create a data volume for account: %s" % self.account_2.account.name) + # Create a second volume from the ROOTDISK (Account 2) + volume_3 = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account_2.account.name, + domainid=self.account_2.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.cleanup.append(volume_3) + # Verify Volume state + self.assertEqual( + volume_3.state in [ + 'Allocated', + 'Ready' + ], + True, + "Check Volume state is Ready or not" + ) + return + + def test_05_templates_per_account(self): + """Test Templates limit per account + """ + + # Validate the following + # 1. Set templates=1 limit for account 1. + # 2. Try to create 2 templates in account 1. Verify account with limit + # should be denied to create more than 1 template. + # 3. Try to create 2 templates in account 2. Verify account 2 should be + # able to create template without any error + + self.debug( + "Updating template resource limit for account: %s" % + self.account_1.account.name) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 4, # Template + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + max=1 + ) + + self.debug( + "Updating volume resource limit for account: %s" % + self.account_1.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_1.account.name, + domainid=self.account_1.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + self.debug( + "Deploying virtual machine for account: %s" % + self.account_2.account.name) + # Create VM for second account + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_2.account.name, + domainid=self.account_2.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_2) + # Verify VM state + self.assertEqual( + virtual_machine_2.state, + 'Running', + "Check VM state is Running or not" + ) + + virtual_machine_1.stop(self.apiclient) + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=virtual_machine_1.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug( + "Creating template from volume: %s" % volume.id) + # Create a template from the ROOTDISK (Account 1) + template_1 = Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + ) + + self.cleanup.append(template_1) + # Verify Template state + self.assertEqual( + template_1.isready, + True, + "Check Template is in ready state or not" + ) + + # Exception should be raised for second snapshot (account_1) + with self.assertRaises(Exception): + Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + account=self.account_1.account.name, + domainid=self.account_1.account.domainid, + ) + virtual_machine_2.stop(self.apiclient) + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=virtual_machine_2.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug( + "Creating template from volume: %s" % volume.id) + # Create a snapshot from the ROOTDISK (Account 1) + template_2 = Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + account=self.account_2.account.name, + domainid=self.account_2.account.domainid, + ) + + self.cleanup.append(template_2) + # Verify Template state + self.assertEqual( + template_2.isready, + True, + "Check Template is in ready state or not" + ) + self.debug( + "Creating template from volume: %s" % volume.id) + # Create a second volume from the ROOTDISK (Account 2) + template_3 = Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + account=self.account_2.account.name, + domainid=self.account_2.account.domainid, + ) + + self.cleanup.append(template_3) + # Verify Template state + self.assertEqual( + template_3.isready, + True, + "Check Template is in ready state or not" + ) + return + + +class TestResourceLimitsDomain(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestResourceLimitsDomain, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + # Create Domains, Account etc + cls.domain = Domain.create( + cls.api_client, + cls.services["domain"] + ) + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + # Create Service offering and disk offerings etc + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account, + cls.domain + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_vm_per_domain(self): + """Test VM limit per domain + """ + + # Validate the following + # 1. Set max VM per domain to 2 + # 2. Create account and start 2 VMs. Verify VM state is Up and Running + # 3. Try to create 3rd VM instance. The appropriate error or alert + # should be raised + + self.debug( + "Updating instance resource limits for domain: %s" % + self.account.account.domainid) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 0, # Instance + domainid=self.account.account.domainid, + max=2 + ) + + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_2) + # Verify VM state + self.assertEqual( + virtual_machine_2.state, + 'Running', + "Check VM state is Running or not" + ) + # Exception should be raised for second instance + with self.assertRaises(Exception): + VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account_1.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + return + + def test_01_publicip_per_domain(self): + """Test Public IP limit per domain + """ + + # Validate the following + # 1. set max no of IPs per domain to 2. + # 2. Create an account in this domain + # 3. Create 1 VM in this domain + # 4. Acquire 1 IP in the domain. IP should be successfully acquired + # 5. Try to acquire 3rd IP in this domain. It should give the user an + # appropriate error and an alert should be generated. + + self.debug( + "Updating public IP resource limits for domain: %s" % + self.account.account.domainid) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 1, # Public Ip + domainid=self.account.account.domainid, + max=2 + ) + + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + self.debug("Associating public IP for account: %s" % self.account.account.name) + public_ip_1 = PublicIPAddress.create( + self.apiclient, + virtual_machine_1.account, + virtual_machine_1.zoneid, + virtual_machine_1.domainid, + self.services["server"] + ) + self.cleanup.append(public_ip_1) + # Verify Public IP state + self.assertEqual( + public_ip_1.ipaddress.state in [ + 'Allocated', + 'Allocating' + ], + True, + "Check Public IP state is allocated or not" + ) + + # Exception should be raised for second Public IP + with self.assertRaises(Exception): + public_ip_2 = PublicIPAddress.create( + self.apiclient, + virtual_machine_1.account, + virtual_machine_1.zoneid, + virtual_machine_1.domainid, + self.services["server"] + ) + return + + def test_03_snapshots_per_domain(self): + """Test Snapshot limit per domain + """ + + # Validate the following + # 1. set max no of snapshots per domain to 1. + # 2. Create an account in this domain + # 3. Create 1 VM in this domain + # 4. Create one snapshot in the domain. Snapshot should be successfully + # created + # 5. Try to create another snapshot in this domain. It should give the + # user an appropriate error and an alert should be generated. + + self.debug( + "Updating snapshot resource limits for domain: %s" % + self.account.account.domainid) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 3, # Snapshot + domainid=self.account.account.domainid, + max=1 + ) + + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=virtual_machine_1.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating snapshot from volume: %s" % volumes[0].id) + # Create a snapshot from the ROOTDISK + snapshot_1 = Snapshot.create(self.apiclient, + volumes[0].id, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.cleanup.append(snapshot_1) + # Verify Snapshot state + self.assertEqual( + snapshot_1.state in [ + 'BackedUp', + 'CreatedOnPrimary' + ], + True, + "Check Snapshot state is Running or not" + ) + + # Exception should be raised for second snapshot + with self.assertRaises(Exception): + Snapshot.create(self.apiclient, + volumes[0].id, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + return + + def test_04_volumes_per_domain(self): + """Test Volumes limit per domain + """ + + # Validate the following + # 1. set max no of volume per domain to 1. + # 2. Create an account in this domain + # 3. Create 1 VM in this domain + # 4. Try to Create another VM in the domain. It should give the user an + # appropriate error that Volume limit is exhausted and an alert + # should be generated. + + self.debug( + "Updating volume resource limits for domain: %s" % + self.account.account.domainid) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 2, # Volume + domainid=self.account.account.domainid, + max=2 + ) + + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + + # Exception should be raised for second volume + with self.assertRaises(Exception): + Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + diskofferingid=self.disk_offering.id + ) + return + + def test_05_templates_per_domain(self): + """Test Templates limit per domain + """ + + # Validate the following + # 1. set max no of templates per domain to 2. + # 2. Create an account in this domain + # 3. Create 2 templates in this domain. Both template should be in + # ready state + # 4. Try create 3rd template in the domain. It should give the user an + # appropriate error and an alert should be generated. + + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 2, # Volume + domainid=self.account.account.domainid, + max=5 + ) + + self.debug( + "Updating template resource limits for domain: %s" % + self.account.account.domainid) + # Set usage_vm=1 for Account 1 + update_resource_limit( + self.apiclient, + 4, # Template + domainid=self.account.account.domainid, + max=2 + ) + + self.debug("Deploying VM for account: %s" % self.account.account.name) + virtual_machine_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine_1) + # Verify VM state + self.assertEqual( + virtual_machine_1.state, + 'Running', + "Check VM state is Running or not" + ) + virtual_machine_1.stop(self.apiclient) + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=virtual_machine_1.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check for list volume response return valid data" + ) + volume = volumes[0] + + self.debug("Creating template from volume: %s" % volume.id) + # Create a template from the ROOTDISK + template_1 = Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.cleanup.append(template_1) + # Verify Template state + self.assertEqual( + template_1.isready, + True, + "Check Template is in ready state or not" + ) + self.debug("Creating template from volume: %s" % volume.id) + # Create a template from the ROOTDISK + template_2 = Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.cleanup.append(template_2) + # Verify Template state + self.assertEqual( + template_2.isready, + True, + "Check Template is in ready state or not" + ) + + # Exception should be raised for second template + with self.assertRaises(Exception): + Template.create( + self.apiclient, + self.services["template"], + volumeid=volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + return + + +class TestResources(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super( + TestResources, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_zones(self): + """Check the status of zones""" + + # Validate the following + # 1. List zones + # 2. Check allocation state is "enabled" or not + + zones = Zone.list( + self.apiclient, + id=self.zone.id, + listall=True + ) + self.assertEqual( + isinstance(zones, list), + True, + "Check if listZones returns a valid response" + ) + for zone in zones: + self.assertEqual( + zone.allocationstate, + 'Enabled', + "Zone allocation state should be enabled" + ) + return + + def test_02_pods(self): + """Check the status of pods""" + + # Validate the following + # 1. List pods + # 2. Check allocation state is "enabled" or not + + pods = Pod.list( + self.apiclient, + zoneid=self.zone.id, + listall=True + ) + self.assertEqual( + isinstance(pods, list), + True, + "Check if listPods returns a valid response" + ) + for pod in pods: + self.assertEqual( + pod.allocationstate, + 'Enabled', + "Pods allocation state should be enabled" + ) + return + + def test_03_clusters(self): + """Check the status of clusters""" + + # Validate the following + # 1. List clusters + # 2. Check allocation state is "enabled" or not + + clusters = Cluster.list( + self.apiclient, + zoneid=self.zone.id, + listall=True + ) + self.assertEqual( + isinstance(clusters, list), + True, + "Check if listClusters returns a valid response" + ) + for cluster in clusters: + self.assertEqual( + cluster.allocationstate, + 'Enabled', + "Clusters allocation state should be enabled" + ) + return + + def test_04_hosts(self): + """Check the status of hosts""" + + # Validate the following + # 1. List hosts with type=Routing + # 2. Check state is "Up" or not + + hosts = Host.list( + self.apiclient, + zoneid=self.zone.id, + type='Routing', + listall=True + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check if listHosts returns a valid response" + ) + for host in hosts: + self.assertEqual( + host.state, + 'Up', + "Host should be in Up state and running" + ) + return + + def test_05_storage_pools(self): + """Check the status of Storage pools""" + + # Validate the following + # 1. List storage pools for the zone + # 2. Check state is "enabled" or not + + storage_pools = StoragePool.list( + self.apiclient, + zoneid=self.zone.id, + listall=True + ) + self.assertEqual( + isinstance(storage_pools, list), + True, + "Check if listStoragePools returns a valid response" + ) + for storage_pool in storage_pools: + self.assertEqual( + storage_pool.state, + 'Up', + "storage pool should be in Up state and running" + ) + return + + def test_06_secondary_storage(self): + """Check the status of secondary storage""" + + # Validate the following + # 1. List secondary storage + # 2. Check state is "Up" or not + + sec_storages = Host.list( + self.apiclient, + zoneid=self.zone.id, + type='SecondaryStorage', + listall=True + ) + self.assertEqual( + isinstance(sec_storages, list), + True, + "Check if listHosts returns a valid response" + ) + for sec_storage in sec_storages: + self.assertEqual( + sec_storage.state, + 'Up', + "Secondary storage should be in Up state" + ) + return diff --git a/test/integration/component/test_routers.py b/test/integration/component/test_routers.py new file mode 100644 index 00000000000..05792336d69 --- /dev/null +++ b/test/integration/component/test_routers.py @@ -0,0 +1,1201 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for routers +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + +#Import System modules +import time + + +class Services: + """Test router Services + """ + + def __init__(self): + self.services = { + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "virtual_machine": + { + "displayname": "Test VM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + # Hypervisor type should be same as + # hypervisor type of cluster + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "host": { + "username": "root", + "password": "fr3sca", + "publicport": 22, + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "testuser", + "password": "password", + }, + "natrule": + { + "privateport": 22, + "publicport": 222, + "protocol": "TCP" + }, + "lbrule": + { + "name": "SSH", + "alg": "roundrobin", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + }, + "fw_rule":{ + "startport": 1, + "endport": 6000, + "cidr": '55.55.0.0/11', + # Any network (For creating FW rule + }, + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + # Used for Get_Template : CentOS 5.3 (64 bit) + "mode": 'advanced', # Networking mode: Advanced, basic + } + + +class TestRouterServices(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super(TestRouterServices, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.vm_1 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=cls.template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.vm_2 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=cls.template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestRouterServices, cls).getClsTestClient().getApiClient() + #Clean up, terminate the created templates + cleanup_resources(cls.api_client, cls.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self._cleanup = [] + return + + def test_01_AdvancedZoneRouterServices(self): + """Test advanced zone router services + """ + + # Validate the following: + # 1. Verify that list of services provided by this network are running + # a. DNS + # b. DHCP + # c. Gateway + # d. Firewall + # e. LB + # f. VPN + # g. userdata + # 2. wait for router to start and guest network to be created + # a. listRouters account=user, domainid=1 (router state=Running) + # b. listNetworks account=user domainid=1 (network state=Implemented) + # c. listVirtualMachines account=user domainid=1 (VM state=Running) + # 3. listNetwork + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + self.assertNotEqual( + len(routers), + 0, + "Check list router response" + ) + for router in routers: + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + self.debug("Router ID: %s & Router state: %s" % ( + router.id, + router.state + )) + # Network state associated with account should be 'Implemented' + networks = list_networks( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.assertEqual( + isinstance(networks, list), + True, + "Check for list networks response return valid data" + ) + + self.assertNotEqual( + len(networks), + 0, + "Check list networks response" + ) + for network in networks: + self.assertIn( + network.state, + ['Implemented','Allocated'], + "Check list network response for network state" + ) + self.debug("Network ID: %s & Network state: %s" % ( + network.id, + network.state + )) + # VM state associated with account should be 'Running' + virtual_machines = list_virtual_machines( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(virtual_machines, list), + True, + "Check for list virtual machines response return valid data" + ) + self.assertNotEqual( + len(virtual_machines), + 0, + "Check list virtual machines response" + ) + for virtual_machine in virtual_machines: + self.assertEqual( + virtual_machine.state, + 'Running', + "Check list VM response for Running state" + ) + self.debug("VM ID: %s & VM state: %s" % ( + virtual_machine.id, + virtual_machine.state + )) + + # Check status of DNS, DHCP, FIrewall, LB VPN processes + networks = list_networks( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.assertEqual( + isinstance(networks, list), + True, + "Check for list networks response return valid data" + ) + self.assertNotEqual( + len(networks), + 0, + "Check list networks response" + ) + # Load Balancer, Userdata, VPN, Firewall, Gateway, DNS processes should + # be running + for network in networks: + self.assertEqual( + 'Lb' in str(network.service), + True, + "Check Load balancing process in list networks" + ) + self.assertEqual( + 'UserData' in str(network.service), + True, + "Check UserData service in list networks" + ) + self.assertEqual( + 'Vpn' in str(network.service), + True, + "Check Vpn service in list networks" + ) + self.assertEqual( + 'Firewall' in str(network.service), + True, + "Check Firewall service in list networks" + ) + self.assertEqual( + 'Dns' in str(network.service), + True, + "Check Dns service in list networks" + ) + return + + def test_02_NetworkGarbageCollection(self): + """Test network garbage collection + """ + + # Validate the following + # 1. wait for router to start and guest network to be created + # a.listRouters account=user, domainid=1 (router state=Running) + # b.listNetworks account=user domainid=1 (network state=Implemented) + # c.listVirtualMachines account=user domainid=1 (VM states=Running) + # 4. stopVirtualMachines (stop all VMs in this account) + # 5. wait for VMs to stop-listVirtualMachines account=user, domainid=1 + # (Both VM states = Stopped) + # 6. wait for network.gc.interval*2 seconds (600s) + # 7. listRouters account=user, domainid=1 + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + self.assertNotEqual( + len(routers), + 0, + "Check list router response" + ) + # Router associated with account should be in running state + for router in routers: + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + self.debug("Router ID: %s & Router state: %s" % ( + router.id, + router.state + )) + # Network state associated with account should be 'Implemented' + networks = list_networks( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.assertEqual( + isinstance(networks, list), + True, + "Check for list networks response return valid data" + ) + self.assertNotEqual( + len(networks), + 0, + "Check list networks response" + ) + # Check if network in 'Implemented' state + for network in networks: + self.assertIn( + network.state, + ['Implemented','Allocated'], + "Check list network response for network state" + ) + self.debug("Network ID: %s & Network state: %s" % ( + network.id, + network.state + )) + # VM state associated with account should be 'Running' + virtual_machines = list_virtual_machines( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(virtual_machines, list), + True, + "Check for list virtual machines response return valid data" + ) + self.assertNotEqual( + len(virtual_machines), + 0, + "Check list virtual machines response" + ) + for virtual_machine in virtual_machines: + self.assertEqual( + virtual_machine.state, + 'Running', + "Check list VM response for Running state" + ) + self.debug("VM ID: %s & VM state: %s" % ( + virtual_machine.id, + virtual_machine.state + )) + # Stop virtual machine + cmd = stopVirtualMachine.stopVirtualMachineCmd() + cmd.id = virtual_machine.id + self.apiclient.stopVirtualMachine(cmd) + + gcinterval = list_configurations( + self.apiclient, + name='network.gc.interval' + ) + self.assertEqual( + isinstance(gcinterval, list), + True, + "Check for list intervals response return valid data" + ) + self.debug("network.gc.interval: %s" % gcinterval[0].value) + + gcwait = list_configurations( + self.apiclient, + name='network.gc.wait' + ) + self.assertEqual( + isinstance(gcwait, list), + True, + "Check for list intervals response return valid data" + ) + self.debug("network.gc.wait: %s" % gcwait[0].value) + + total_wait = int(gcinterval[0].value) + int (gcwait[0].value) + # Router is stopped after (network.gc.interval *2) time. Wait for + # (network.gc.interval+network.gc.wait) * 2 for moving router to 'Stopped' + time.sleep(total_wait * 2) + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + self.assertNotEqual( + len(routers), + 0, + "Check list router response" + ) + for router in routers: + self.assertEqual( + router.state, + 'Stopped', + "Check list router response for router state" + ) + self.debug("Router ID: %s & Router state: %s" % ( + router.id, + router.state + )) + # Cleanup Vm_2 - Not required for further tests + self._cleanup.append(self.vm_2) + return + + def test_03_RouterStartOnVmDeploy(self): + """Test router start on VM deploy + """ + # Validate the following + # 1. deployVirtualMachine in the account + # 2. listVirtualMachines account=user, domainid=1 + # 3. when listVirtualMachines reports the userVM to be in state=Running + # 4. listRouters should report router to have come back to "Running" state + # 5. All other VMs in the account should remain in "Stopped" state + + vm = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed a VM with ID: %s" % vm.id) + + virtual_machines = list_virtual_machines( + self.apiclient, + id=vm.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(virtual_machines, list), + True, + "Check for list virtual machines response return valid data" + ) + + self.assertNotEqual( + len(virtual_machines), + 0, + "Check list virtual machines response" + ) + # VM state should be 'Running' + for virtual_machine in virtual_machines: + self.assertEqual( + virtual_machine.state, + 'Running', + "Check list VM response for Running state" + ) + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + + self.assertNotEqual( + len(routers), + 0, + "Check list router response" + ) + # Routers associated with account should be 'Running' after deployment + # of VM + for router in routers: + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + self.debug("Router ID: %s & Router state: %s" % ( + router.id, + router.state + )) + + # All other VMs (VM_1) should be in 'Stopped' + virtual_machines = list_virtual_machines( + self.apiclient, + id=self.vm_1.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(virtual_machines, list), + True, + "Check for list VMs response return valid data" + ) + + self.assertNotEqual( + len(virtual_machines), + 0, + "Check list virtual machines response" + ) + for virtual_machine in virtual_machines: + self.assertEqual( + virtual_machine.state, + 'Stopped', + "Check list VM response for Stopped state" + ) + self.debug("VM ID: %s & VM state: %s" % ( + virtual_machine.id, + virtual_machine.state + )) + return + + + +class TestRouterStopCreatePF(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super(TestRouterStopCreatePF, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.vm_1 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestRouterStopCreatePF, cls).getClsTestClient().getApiClient() + # Clean up, terminate the created resources + cleanup_resources(cls.api_client, cls.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def tearDown(self): + try: + # Clean up, terminate the created resources + cleanup_resources(self.apiclient, self._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self._cleanup = [] + return + + def test_01_RouterStopCreatePF(self): + """Test router stop create port forwarding + """ + + # validate the following + # 1. wait for router to start, guest network to be implemented and + # VM to report Running + # 2. stopRouter for this account + # 3. wait for listRouters to report Router as 'Stopped' + # 4. listPublicIpAddresses account=user, domainid=1 - pick ipaddressid + # 5. createPortForwardingRule (ipaddressid from step 5.) + # a. for port 22 (ssh) for user VM deployed in step 1. + # b. public port 222 , private port 22 + # 6. startRouter stopped for this account + # 7. wait for listRouters to show router as Running + + # Get router details associated for that account + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + self.assertNotEqual( + len(routers), + 0, + "Check list router response" + ) + router = routers[0] + + self.debug("Stopping router ID: %s" % router.id) + + #Stop the router + cmd = stopRouter.stopRouterCmd() + cmd.id = router.id + self.apiclient.stopRouter(cmd) + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + router = routers[0] + + self.assertEqual( + router.state, + 'Stopped', + "Check list router response for router state" + ) + + public_ips = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(public_ips, list), + True, + "Check for list public IPs response return valid data" + ) + public_ip = public_ips[0] + + self.debug("Creating NAT rule for VM ID: %s" % self.vm_1.id) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + self.vm_1, + self.services["natrule"], + public_ip.id + ) + + self.debug("Starting router ID: %s" % router.id) + #Start the router + cmd = startRouter.startRouterCmd() + cmd.id = router.id + self.apiclient.startRouter(cmd) + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + router = routers[0] + + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + # NAT Rule should be in Active state after router start + nat_rules = list_nat_rules( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(nat_rules, list), + True, + "Check for list NAT rules response return valid data" + ) + self.assertEqual( + nat_rules[0].state, + 'Active', + "Check list port forwarding rules" + ) + try: + + self.debug("SSH into VM with ID: %s" % nat_rule.ipaddress) + + self.vm_1.ssh_port = nat_rule.publicport + self.vm_1.get_ssh_client(nat_rule.ipaddress) + except Exception as e: + self.fail( + "SSH Access failed for %s: %s" % \ + (self.vm_1.ipaddress, e) + ) + return + +class TestRouterStopCreateLB(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super(TestRouterStopCreateLB, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.vm_1 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestRouterStopCreateLB, cls).getClsTestClient().getApiClient() + #Clean up, terminate the created resources + cleanup_resources(cls.api_client, cls.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self._cleanup = [] + return + + def test_01_RouterStopCreateLB(self): + """Test router stop create Load balancing + """ + + # validate the following + # 1. listLoadBalancerRules (publicipid=ipaddressid of source NAT) + # 2. rule should be for port 2222 as applied and + # should be in state=Active + # 3. ssh access should be allowed to the userVMs over the source NAT IP + # and port 2222 + + # Get router details associated for that account + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + + self.assertNotEqual( + len(routers), + 0, + "Check list router response" + ) + + router = routers[0] + + self.debug("Stopping router with ID: %s" % router.id) + #Stop the router + cmd = stopRouter.stopRouterCmd() + cmd.id = router.id + self.apiclient.stopRouter(cmd) + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + router = routers[0] + + self.assertEqual( + router.state, + 'Stopped', + "Check list router response for router state" + ) + + public_ips = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(public_ips, list), + True, + "Check for list public IPs response return valid data" + ) + public_ip = public_ips[0] + self.debug("Creating LB rule for public IP: %s" % public_ip.id) + #Create Load Balancer rule and assign VMs to rule + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + public_ip.id, + accountid=self.account.account.name + ) + self.debug("Assigning VM %s to LB rule: %s" % ( + self.vm_1.id, + lb_rule.id + )) + lb_rule.assign(self.apiclient, [self.vm_1]) + + #Start the router + cmd = startRouter.startRouterCmd() + cmd.id = router.id + self.apiclient.startRouter(cmd) + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + router = routers[0] + + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + # After router start, LB RUle should be in Active state + lb_rules = list_lb_rules( + self.apiclient, + id=lb_rule.id + ) + self.assertEqual( + isinstance(lb_rules, list), + True, + "Check for list LB rules response return valid data" + ) + self.assertEqual( + lb_rules[0].state, + 'Active', + "Check list load balancing rules" + ) + self.assertEqual( + lb_rules[0].publicport, + str(self.services["lbrule"]["publicport"]), + "Check list load balancing rules" + ) + + try: + self.debug("SSH into VM with IP: %s" % public_ip.ipaddress) + self.vm_1.ssh_port = self.services["lbrule"]["publicport"] + self.vm_1.get_ssh_client(public_ip.ipaddress) + except Exception as e: + self.fail( + "SSH Access failed for %s: %s" % \ + (self.vm_1.ipaddress, e) + ) + return + + +class TestRouterStopCreateFW(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super(TestRouterStopCreateFW, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.vm_1 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestRouterStopCreateFW, cls).getClsTestClient().getApiClient() + #Clean up, terminate the created templates + cleanup_resources(cls.api_client, cls.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self._cleanup = [] + return + + def test_01_RouterStopCreateFW(self): + """Test router stop create Firewall rule + """ + + # validate the following + # 1. 1. listFirewallRules (filter by ipaddressid of sourcenat) + # 2. rule should be for ports 1-600 and in state=Active + # (optional backend) + # 3. verify on router using iptables -t nat -nvx if rules are applied + + # Get the router details associated with account + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + + self.assertNotEqual( + len(routers), + 0, + "Check list router response" + ) + + router = routers[0] + + self.debug("Stopping the router: %s" % router.id) + #Stop the router + cmd = stopRouter.stopRouterCmd() + cmd.id = router.id + self.apiclient.stopRouter(cmd) + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + router = routers[0] + + self.assertEqual( + router.state, + 'Stopped', + "Check list router response for router state" + ) + + public_ips = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(public_ips, list), + True, + "Check for list public IP response return valid data" + ) + public_ip = public_ips[0] + + #Create Firewall rule with configurations from settings file + fw_rule = FireWallRule.create( + self.apiclient, + ipaddressid=public_ip.id, + protocol='TCP', + cidrlist=[self.services["fw_rule"]["cidr"]], + startport=self.services["fw_rule"]["startport"], + endport=self.services["fw_rule"]["endport"] + ) + self.debug("Created firewall rule: %s" % fw_rule.id) + + self.debug("Starting the router: %s" % router.id) + #Start the router + cmd = startRouter.startRouterCmd() + cmd.id = router.id + self.apiclient.startRouter(cmd) + + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check for list routers response return valid data" + ) + + router = routers[0] + + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + # After Router start, FW rule should be in Active state + fw_rules = list_firewall_rules( + self.apiclient, + id=fw_rule.id, + ) + self.assertEqual( + isinstance(fw_rules, list), + True, + "Check for list FW rules response return valid data" + ) + + self.assertEqual( + fw_rules[0].state, + 'Active', + "Check list load balancing rules" + ) + self.assertEqual( + fw_rules[0].startport, + str(self.services["fw_rule"]["startport"]), + "Check start port of firewall rule" + ) + + self.assertEqual( + fw_rules[0].endport, + str(self.services["fw_rule"]["endport"]), + "Check end port of firewall rule" + ) + hosts = list_hosts( + self.apiclient, + id=router.hostid, + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check for list hosts response return valid data" + ) + host = hosts[0] + # For DNS and DHCP check 'dnsmasq' process status + result = get_process_status( + host.ipaddress, + self.services['host']["publicport"], + self.services['host']["username"], + self.services['host']["password"], + router.linklocalip, + 'iptables -t nat -L' + ) + self.debug("iptables -t nat -L: %s" % result) + self.debug("Public IP: %s" % public_ip.ipaddress) + res = str(result) + self.assertEqual( + res.count(str(public_ip.ipaddress)), + 1, + "Check public IP address" + ) + return + diff --git a/test/integration/component/test_security_groups.py b/test/integration/component/test_security_groups.py new file mode 100644 index 00000000000..60cab0ee947 --- /dev/null +++ b/test/integration/component/test_security_groups.py @@ -0,0 +1,1628 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 + +""" P1 for Security groups +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + +#Import System modules +import time +import subprocess + + +class Services: + """Test Security groups Services + """ + + def __init__(self): + self.services = { + "disk_offering":{ + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "fr3sca", + }, + "virtual_machine": { + # Create a small virtual machine instance with disk offering + "displayname": "Test VM", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + "userdata": 'This is sample data', + }, + "host": { + "publicport": 22, + "username": "root", # Host creds for SSH + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "security_group": { + "name": 'SSH', + "protocol": 'TCP', + "startport": 22, + "endport": 22, + "cidrlist": '0.0.0.0/0', + }, + "security_group_2": { + "name": 'ICMP', + "protocol": 'ICMP', + "startport": -1, + "endport": -1, + "cidrlist": '0.0.0.0/0', + }, + "ostypeid": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + # CentOS 5.3 (64-bit) + "sleep": 60, + "timeout": 10, + "mode":'basic', + # Networking mode: Basic or Advanced + } + + +class TestDefaultSecurityGroup(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestDefaultSecurityGroup, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestDefaultSecurityGroup, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_deployVM_InDefaultSecurityGroup(self): + """Test deploy VM in default security group + """ + + # Validate the following: + # 1. deploy Virtual machine using admin user + # 2. listVM should show a VM in Running state + # 3. listRouters should show one router running + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM with ID: %s" % self.virtual_machine.id) + self.cleanup.append(self.virtual_machine) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check for list VM response" + ) + vm_response = list_vm_response[0] + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + + self.assertEqual( + + vm_response.id, + self.virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + + self.assertEqual( + vm_response.displayname, + self.virtual_machine.displayname, + "Check virtual machine displayname in listVirtualMachines" + ) + + # Verify List Routers response for account + self.debug( + "Verify list routers response for account: %s" \ + % self.account.account.name + ) + routers = list_routers( + self.apiclient, + zoneid=self.zone.id, + listall=True + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check for list Routers response" + ) + + self.debug("Router Response: %s" % routers) + self.assertEqual( + len(routers), + 1, + "Check virtual router is created for account or not" + ) + return + + def test_02_listSecurityGroups(self): + """Test list security groups for admin account + """ + + # Validate the following: + # 1. listSecurityGroups in admin account + # 2. There should be one security group (default) listed for the admin account + # 3. No Ingress Rules should be part of the default security group + + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + self.assertNotEqual( + len(sercurity_groups), + 0, + "Check List Security groups response" + ) + self.debug("List Security groups response: %s" % + str(sercurity_groups)) + self.assertEqual( + hasattr(sercurity_groups, 'ingressrule'), + False, + "Check ingress rule attribute for default security group" + ) + return + + def test_03_accessInDefaultSecurityGroup(self): + """Test access in default security group + """ + + # Validate the following: + # 1. deploy Virtual machine using admin user + # 2. listVM should show a VM in Running state + # 3. listRouters should show one router running + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Deployed VM with ID: %s" % self.virtual_machine.id) + self.cleanup.append(self.virtual_machine) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check for list VM response" + ) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.virtual_machine.id + ) + + vm_response = list_vm_response[0] + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + + self.assertEqual( + + vm_response.id, + self.virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + + self.assertEqual( + vm_response.displayname, + self.virtual_machine.displayname, + "Check virtual machine displayname in listVirtualMachines" + ) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.debug("List Security groups response: %s" % + str(sercurity_groups)) + self.assertNotEqual( + len(sercurity_groups), + 0, + "Check List Security groups response" + ) + self.assertEqual( + hasattr(sercurity_groups, 'ingressrule'), + False, + "Check ingress rule attribute for default security group" + ) + + # SSH Attempt to VM should fail + with self.assertRaises(Exception): + self.debug("SSH into VM: %s" % self.virtual_machine.ssh_ip) + ssh = remoteSSHClient.remoteSSHClient( + self.virtual_machine.ssh_ip, + self.virtual_machine.ssh_port, + self.virtual_machine.username, + self.virtual_machine.password + ) + return + + +class TestAuthorizeIngressRule(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestAuthorizeIngressRule, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestAuthorizeIngressRule, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_authorizeIngressRule(self): + """Test authorize ingress rule + """ + + # Validate the following: + #1. Create Security group for the account. + #2. Createsecuritygroup (ssh-incoming) for this account + #3. authorizeSecurityGroupIngress to allow ssh access to the VM + #4. deployVirtualMachine into this security group (ssh-incoming) + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + self.debug("Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.id) + self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + return + + +class TestRevokeIngressRule(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestRevokeIngressRule, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestRevokeIngressRule, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_revokeIngressRule(self): + """Test revoke ingress rule + """ + + # Validate the following: + #1. Create Security group for the account. + #2. Createsecuritygroup (ssh-incoming) for this account + #3. authorizeSecurityGroupIngress to allow ssh access to the VM + #4. deployVirtualMachine into this security group (ssh-incoming) + #5. Revoke the ingress rule, SSH access should fail + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + # Authorize Security group to SSH to VM + self.debug("Authorizing ingress rule for sec group ID: %s for ssh access" + % security_group.id) + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Should be able to SSH VM + try: + self.debug("SSH into VM: %s" % self.virtual_machine.id) + self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + self.debug("Revoking ingress rule for sec group ID: %s for ssh access" + % security_group.id) + # Revoke Security group to SSH to VM + result = security_group.revoke( + self.apiclient, + id=ssh_rule["ruleid"] + ) + + # SSH Attempt to VM should fail + with self.assertRaises(Exception): + self.debug("SSH into VM: %s" % self.virtual_machine.id) + remoteSSHClient.remoteSSHClient( + self.virtual_machine.ssh_ip, + self.virtual_machine.ssh_port, + self.virtual_machine.username, + self.virtual_machine.password + ) + return + + +class TestDhcpOnlyRouter(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestDhcpOnlyRouter, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestDhcpOnlyRouter, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_dhcpOnlyRouter(self): + """Test router services for user account + """ + # Validate the following + #1. List routers for any user account + #2. The only service supported by this router should be dhcp + + # Find router associated with user account + list_router_response = list_routers( + self.apiclient, + zoneid=self.zone.id, + listall=True + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + hosts = list_hosts( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + virtualmachineid=self.virtual_machine.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list host returns a valid list" + ) + host = hosts[0] + + self.debug("Router ID: %s, state: %s" % (router.id, router.state)) + + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + + result = get_process_status( + host.ipaddress, + self.services['host']["publicport"], + self.services['host']["username"], + self.services['host']["password"], + router.linklocalip, + "service dnsmasq status" + ) + res = str(result) + self.debug("Dnsmasq process status: %s" % res) + + self.assertEqual( + res.count("running"), + 1, + "Check dnsmasq service is running or not" + ) + return + + +class TestdeployVMWithUserData(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestdeployVMWithUserData, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + + cls.services["domainid"] = cls.domain.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestdeployVMWithUserData, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_deployVMWithUserData(self): + """Test Deploy VM with User data""" + + # Validate the following + # 1. CreateAccount of type user + # 2. CreateSecurityGroup ssh-incoming + # 3. authorizeIngressRule to allow ssh-access + # 4. deployVirtualMachine into this group with some base64 encoded user-data + # 5. wget http://10.1.1.1/latest/user-data to get the latest userdata from the + # router for this VM + + # Find router associated with user account + list_router_response = list_routers( + self.apiclient, + zoneid=self.zone.id, + listall=True + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + + self.debug( + "Authorize Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + + # Authorize Security group to SSH to VM + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + # Should be able to SSH VM + try: + self.debug( + "SSH to VM with IP Address: %s"\ + % self.virtual_machine.ssh_ip + ) + + ssh = self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + cmds = [ + "wget http://%s/latest/user-data" % router.guestipaddress, + "cat user-data", + ] + for c in cmds: + result = ssh.execute(c) + self.debug("%s: %s" % (c, result)) + + res = str(result) + self.assertEqual( + res.count(self.services["virtual_machine"]["userdata"]), + 1, + "Verify user data" + ) + return + + +class TestDeleteSecurityGroup(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + + self.services = Services().services + + # Get Zone, Domain and templates + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + + template = get_template( + self.apiclient, + self.zone.id, + self.services["ostypeid"] + ) + + self.services["domainid"] = self.domain.id + self.services["virtual_machine"]["zoneid"] = self.zone.id + self.services["virtual_machine"]["template"] = template.id + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + self.services["account"] = self.account.account.name + self.cleanup = [ + self.account, + self.service_offering + ] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestDeleteSecurityGroup, cls).getClsTestClient().getApiClient() + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestDeleteSecurityGroup, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_delete_security_grp_running_vm(self): + """Test delete security group with running VM""" + + # Validate the following + # 1. createsecuritygroup (ssh-incoming) for this account + # 2. authorizeSecurityGroupIngress to allow ssh access to the VM + # 3. deployVirtualMachine into this security group (ssh-incoming) + # 4. deleteSecurityGroup created in step 1. Deletion should fail + # complaining there are running VMs in this group + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + self.debug( + "Authorize Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + + # Authorize Security group to SSH to VM + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Deleting Security group should raise exception + security_group.delete(self.apiclient) + + #sleep to ensure that Security group is deleted properly + time.sleep(self.services["sleep"]) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + id=security_group.id + ) + self.assertNotEqual( + sercurity_groups, + None, + "Check List Security groups response" + ) + return + + def test_02_delete_security_grp_withoout_running_vm(self): + """Test delete security group without running VM""" + + # Validate the following + # 1. createsecuritygroup (ssh-incoming) for this account + # 2. authorizeSecurityGroupIngress to allow ssh access to the VM + # 3. deployVirtualMachine into this security group (ssh-incoming) + # 4. deleteSecurityGroup created in step 1. Deletion should fail + # complaining there are running VMs in this group + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + + self.debug( + "Authorize Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + # Authorize Security group to SSH to VM + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # Destroy the VM + self.virtual_machine.delete(self.apiclient) + + config = list_configurations( + self.apiclient, + name='expunge.delay' + ) + self.assertEqual( + isinstance(config, list), + True, + "Check list configurations response" + ) + response = config[0] + self.debug("expunge.delay: %s" % response.value) + # Wait for some time more than expunge.delay + time.sleep(int(response.value) * 2) + + # Deleting Security group should raise exception + try: + self.debug("Deleting Security Group: %s" % security_group.id) + security_group.delete(self.apiclient) + except Exception as e: + self.fail("Failed to delete security group - ID: %s" \ + % security_group.id + ) + return + + +class TestIngressRule(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + self.services = Services().services + + # Get Zone, Domain and templates + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + + template = get_template( + self.apiclient, + self.zone.id, + self.services["ostypeid"] + ) + + self.services["domainid"] = self.domain.id + self.services["virtual_machine"]["zoneid"] = self.zone.id + self.services["virtual_machine"]["template"] = template.id + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + self.services["account"] = self.account.account.name + self.cleanup = [ + self.account, + self.service_offering + ] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestIngressRule, cls).getClsTestClient().getApiClient() + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestIngressRule, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_authorizeIngressRule_AfterDeployVM(self): + """Test delete security group with running VM""" + + # Validate the following + # 1. createsecuritygroup (ssh-incoming, 22via22) for this account + # 2. authorizeSecurityGroupIngress to allow ssh access to the VM + # 3. deployVirtualMachine into this security group (ssh-incoming) + # 4. authorizeSecurityGroupIngress to allow ssh access (startport:222 to + # endport:22) to the VM + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + self.debug( + "Authorize Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + + # Authorize Security group to SSH to VM + ingress_rule_1 = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule_1, dict), + True, + "Check ingress rule created properly" + ) + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + self.debug( + "Authorize Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + # Authorize Security group to SSH to VM + ingress_rule_2 = security_group.authorize( + self.apiclient, + self.services["security_group_2"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule_2, dict), + True, + "Check ingress rule created properly" + ) + # SSH should be allowed on 22 & 2222 ports + try: + self.debug("Trying to SSH into VM %s on port %s" % ( + self.virtual_machine.ssh_ip, + self.services["security_group"]["endport"] + )) + self.virtual_machine.get_ssh_client() + + except Exception as e: + self.fail("SSH access failed for ingress rule ID: %s, %s" \ + % (ingress_rule_1["id"], e)) + + # User should be able to ping VM + try: + self.debug("Trying to ping VM %s" % self.virtual_machine.ssh_ip) + result = subprocess.call(['ping', '-c 1', self.virtual_machine.ssh_ip]) + + self.debug("Ping result: %s" % result) + # if ping successful, then result should be 0 + self.assertEqual( + result, + 0, + "Check if ping is successful or not" + ) + + except Exception as e: + self.fail("Ping failed for ingress rule ID: %s, %s" \ + % (ingress_rule_2["id"], e)) + return + + def test_02_revokeIngressRule_AfterDeployVM(self): + """Test Revoke ingress rule after deploy VM""" + + # Validate the following + # 1. createsecuritygroup (ssh-incoming, 22via22) for this account + # 2. authorizeSecurityGroupIngress to allow ssh access to the VM + # 3. deployVirtualMachine into this security group (ssh-incoming) + # 4. authorizeSecurityGroupIngress to allow ssh access (startport:222 + # to endport:22) to the VM + # 5. check ssh access via port 222 + # 6. revokeSecurityGroupIngress to revoke rule added in step 5. verify + # that ssh-access into the VM is now NOT allowed through ports 222 + # but allowed through port 22 + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + + self.debug( + "Authorize Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + + # Authorize Security group to SSH to VM + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + self.debug( + "Authorize Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + + # Authorize Security group to SSH to VM + ingress_rule_2 = security_group.authorize( + self.apiclient, + self.services["security_group_2"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule_2, dict), + True, + "Check ingress rule created properly" + ) + + ssh_rule = (ingress_rule["ingressrule"][0]).__dict__ + icmp_rule = (ingress_rule_2["ingressrule"][0]).__dict__ + + # SSH should be allowed on 22 + try: + self.debug("Trying to SSH into VM %s on port %s" % ( + self.virtual_machine.ssh_ip, + self.services["security_group"]["endport"] + )) + self.virtual_machine.get_ssh_client() + + except Exception as e: + self.fail("SSH access failed for ingress rule ID: %s, %s" \ + % (ssh_rule["ruleid"], e)) + + # User should be able to ping VM + try: + self.debug("Trying to ping VM %s" % self.virtual_machine.ssh_ip) + result = subprocess.call(['ping', '-c 1', self.virtual_machine.ssh_ip]) + + self.debug("Ping result: %s" % result) + # if ping successful, then result should be 0 + self.assertEqual( + result, + 0, + "Check if ping is successful or not" + ) + + except Exception as e: + self.fail("Ping failed for ingress rule ID: %s, %s" \ + % (icmp_rule["ruleid"], e)) + + self.debug( + "Revoke Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + + result = security_group.revoke( + self.apiclient, + id = icmp_rule["ruleid"] + ) + self.debug("Revoke ingress rule result: %s" % result) + + time.sleep(self.services["sleep"]) + # User should not be able to ping VM + try: + self.debug("Trying to ping VM %s" % self.virtual_machine.ssh_ip) + result = subprocess.call(['ping', '-c 1', self.virtual_machine.ssh_ip]) + + self.debug("Ping result: %s" % result) + # if ping successful, then result should be 0 + self.assertNotEqual( + result, + 0, + "Check if ping is successful or not" + ) + + except Exception as e: + self.fail("Ping failed for ingress rule ID: %s, %s" \ + % (icmp_rule["ruleid"], e)) + return + + def test_03_stopStartVM_verifyIngressAccess(self): + """Test Start/Stop VM and Verify ingress rule""" + + # Validate the following + # 1. createsecuritygroup (ssh-incoming, 22via22) for this account + # 2. authorizeSecurityGroupIngress to allow ssh access to the VM + # 3. deployVirtualMachine into this security group (ssh-incoming) + # 4. once the VM is running and ssh-access is available, + # stopVirtualMachine + # 5. startVirtualMachine. After stop start of the VM is complete + # verify that ssh-access to the VM is allowed + + security_group = SecurityGroup.create( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created security group with ID: %s" % security_group.id) + # Default Security group should not have any ingress rule + sercurity_groups = SecurityGroup.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(sercurity_groups, list), + True, + "Check for list security groups response" + ) + + self.assertEqual( + len(sercurity_groups), + 2, + "Check List Security groups response" + ) + + self.debug( + "Authorize Ingress Rule for Security Group %s for account: %s" \ + % ( + security_group.id, + self.account.account.name + )) + + # Authorize Security group to SSH to VM + ingress_rule = security_group.authorize( + self.apiclient, + self.services["security_group"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(ingress_rule, dict), + True, + "Check ingress rule created properly" + ) + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + securitygroupids=[security_group.id] + ) + self.debug("Deploying VM in account: %s" % self.account.account.name) + + # SSH should be allowed on 22 port + try: + self.debug("Trying to SSH into VM %s" % self.virtual_machine.ssh_ip) + self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH access failed for ingress rule ID: %s" \ + % ingress_rule["id"] + ) + + self.virtual_machine.stop(self.apiclient) + + # Sleep to ensure that VM is in stopped state + time.sleep(self.services["sleep"]) + + self.virtual_machine.start(self.apiclient) + + # Sleep to ensure that VM is in running state + time.sleep(self.services["sleep"]) + + # SSH should be allowed on 22 port after restart + try: + self.debug("Trying to SSH into VM %s" % self.virtual_machine.ssh_ip) + self.virtual_machine.get_ssh_client() + except Exception as e: + self.fail("SSH access failed for ingress rule ID: %s" \ + % ingress_rule["id"] + ) + return \ No newline at end of file diff --git a/test/integration/component/test_snapshots.py b/test/integration/component/test_snapshots.py new file mode 100644 index 00000000000..2dd6d90b684 --- /dev/null +++ b/test/integration/component/test_snapshots.py @@ -0,0 +1,1396 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Snapshots +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient + +class Services: + """Test Snapshots Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 200, # in MHz + "memory": 256, # In MBs + }, + "disk_offering": { + "displaytext": "Small Disk", + "name": "Small Disk", + "disksize": 1 + }, + "server": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "mgmt_server": { + "ipaddress": '192.168.100.21', + "username": "root", + "password": "fr3sca", + "port": 22, + }, + "recurring_snapshot": { + "intervaltype": 'HOURLY', + # Frequency of snapshots + "maxsnaps": 1, # Should be min 2 + "schedule": 1, + "timezone": 'US/Arizona', + # Timezone Formats - http://cloud.mindtouch.us/CloudStack_Documentation/Developer's_Guide%3A_CloudStack + }, + "templates": { + "displaytext": 'Template', + "name": 'Template', + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + "templatefilter": 'self', + }, + "diskdevice": "/dev/xvda", + "diskname": "TestDiskServ", + "size": 1, # GBs + + "mount_dir": "/mnt/tmp", + "sub_dir": "test", + "sub_lvl_dir1": "test1", + "sub_lvl_dir2": "test2", + "random_data": "random.data", + + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode" : 'advanced', # Networking mode: Advanced, Basic + } + + +class TestCreateVMsnapshotTemplate(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestCreateVMsnapshotTemplate, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + cls.template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = cls.template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_createVM_snapshotTemplate(self): + """Test create VM, Snapshot and Template + """ + + # Validate the following + # 1. Deploy VM using default template, small service offering + # and small data disk offering. + # 2. Perform snapshot on the root disk of this VM. + # 3. Create a template from snapshot. + # 4. Create a instance from above created template. + # 5. listSnapshots should list the snapshot that was created. + # 6. verify that secondary storage NFS share contains the reqd + # volume under /secondary/snapshots/$accountid/$volumeid/$snapshot_uuid + # 7. verify backup_snap_id was non null in the `snapshots` table + # 8. listTemplates() should return the newly created Template, + # and check for template state as READY" + # 9. listVirtualMachines() command should return the deployed VM. + # State of this VM should be Running. + + #Create Virtual Machine + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Created VM with ID: %s" % self.virtual_machine.id) + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + volume = volumes[0] + + # Create a snapshot from the ROOTDISK + snapshot = Snapshot.create(self.apiclient, volumes[0].id) + self.debug("Snapshot created: ID - %s" % snapshot.id) + self.cleanup.append(snapshot) + + snapshots = list_snapshots( + self.apiclient, + id=snapshot.id + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list snapshots call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check snapshot id in list resources call" + ) + self.debug("select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % snapshot.id) + # Verify backup_snap_id is not NULL + qresultset = self.dbclient.execute( + "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % snapshot.id + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = qresultset[0] + + snapshot_uuid = qresult[0] # backup_snap_id = snapshot UUID + account_id = qresult[1] + volume_id = qresult[2] + + # Generate template from the snapshot + template = Template.create_from_snapshot( + self.apiclient, + snapshot, + self.services["templates"] + ) + self.debug("Created template from snapshot: %s" % template.id) + self.cleanup.append(template) + + templates = list_templates( + self.apiclient, + templatefilter=\ + self.services["templates"]["templatefilter"], + id=template.id + ) + + self.assertNotEqual( + templates, + None, + "Check if result exists in list item call" + ) + + self.assertEqual( + templates[0].isready, + True, + "Check new template state in list templates call" + ) + + # Deploy new virtual machine using template + new_virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + self.debug("Created VM with ID: %s from template: %s" % ( + new_virtual_machine.id, + template.id + )) + self.cleanup.append(new_virtual_machine) + + # Newly deployed VM should be 'Running' + virtual_machines = list_virtual_machines( + self.apiclient, + id=new_virtual_machine.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(virtual_machines, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(virtual_machines), + 0, + "Check list virtual machines response" + ) + for virtual_machine in virtual_machines: + self.assertEqual( + virtual_machine.state, + 'Running', + "Check list VM response for Running state" + ) + # Get the Secondary Storage details from list Hosts + hosts = list_hosts( + self.apiclient, + type='SecondaryStorage', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + uuids = [] + for host in hosts: + # hosts[0].name = "nfs://192.168.100.21/export/test" + parse_url = (host.name).split('/') + # parse_url = ['nfs:', '', '192.168.100.21', 'export', 'test'] + + # Split IP address and export path from name + sec_storage_ip = parse_url[2] + # Sec Storage IP: 192.168.100.21 + + export_path = '/'.join(parse_url[3:]) + # Export path: export/test + + # Sleep to ensure that snapshot is reflected in sec storage + time.sleep(self.services["sleep"]) + try: + # Login to VM to check snapshot present on sec disk + ssh_client = remoteSSHClient.remoteSSHClient( + self.services["mgmt_server"]["ipaddress"], + self.services["mgmt_server"]["port"], + self.services["mgmt_server"]["username"], + self.services["mgmt_server"]["password"], + ) + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s/%s %s" % ( + sec_storage_ip, + export_path, + self.services["mount_dir"] + ), + "ls %s/snapshots/%s/%s" % ( + self.services["mount_dir"], + account_id, + volume_id + ), + ] + for c in cmds: + self.debug("command: %s" % c) + result = ssh_client.execute(c) + self.debug("Result: %s" % result) + + except Exception as e: + self.fail("SSH failed for Management server: %s" % + self.services["mgmt_server"]["ipaddress"]) + uuids.append(result) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + try: + for c in cmds: + self.debug("command: %s" % c) + result = ssh_client.execute(c) + self.debug("Result: %s" % result) + + except Exception as e: + self.fail("SSH failed for Management server: %s" % + self.services["mgmt_server"]["ipaddress"]) + + res = str(uuids) + self.assertEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + + return + + +class TestAccountSnapshotClean(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestAccountSnapshotClean, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + # Get the Root disk of VM + volumes = list_volumes( + cls.api_client, + virtualmachineid=cls.virtual_machine.id, + type='ROOT', + listall=True + ) + volume = volumes[0] + + # Create a snapshot from the ROOTDISK + cls.snapshot = Snapshot.create(cls.api_client, volumes[0].id) + + cls._cleanup = [ + cls.service_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_02_accountSnapshotClean(self): + """Test snapshot cleanup after account deletion + """ + + # Validate the following + # 1. listAccounts API should list out the newly created account + # 2. listVirtualMachines() command should return the deployed VM. + # State of this VM should be "Running" + # 3. a)listSnapshots should list the snapshot that was created. + # b)verify that secondary storage NFS share contains the reqd volume + # under /secondary/snapshots/$accountid/$volumeid/$snapshot_uuid + # 4. a)listAccounts should not list account that is deleted + # b) snapshot image($snapshot_uuid) should be deleted from the + # /secondary/snapshots/$accountid/$volumeid/ + + accounts = list_accounts( + self.apiclient, + id=self.account.account.id + ) + self.assertEqual( + isinstance(accounts, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(accounts), + 0, + "Check list Accounts response" + ) + + # VM should be in 'Running' state + virtual_machines = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + self.assertEqual( + isinstance(virtual_machines, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(virtual_machines), + 0, + "Check list virtual machines response" + ) + for virtual_machine in virtual_machines: + self.debug("VM ID: %s, VM state: %s" % ( + virtual_machine.id, + virtual_machine.state + )) + self.assertEqual( + virtual_machine.state, + 'Running', + "Check list VM response for Running state" + ) + + # Verify the snapshot was created or not + snapshots = list_snapshots( + self.apiclient, + id=self.snapshot.id + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list snapshots call" + ) + self.assertEqual( + snapshots[0].id, + self.snapshot.id, + "Check snapshot id in list resources call" + ) + + # Fetch values from database + qresultset = self.dbclient.execute( + "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % self.snapshot.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB response returns a valid list" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = qresultset[0] + snapshot_uuid = qresult[0] # backup_snap_id = snapshot UUID + account_id = qresult[1] + volume_id = qresult[2] + + # Get the Secondary Storage details from list Hosts + hosts = list_hosts( + self.apiclient, + type='SecondaryStorage', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + uuids = [] + for host in hosts: + # hosts[0].name = "nfs://192.168.100.21/export/test" + parse_url = (host.name).split('/') + # parse_url = ['nfs:', '', '192.168.100.21', 'export', 'test'] + + # Split IP address and export path from name + sec_storage_ip = parse_url[2] + # Sec Storage IP: 192.168.100.21 + + export_path = '/'.join(parse_url[3:]) + # Export path: export/test + + # Sleep to ensure that snapshot is reflected in sec storage + time.sleep(self.services["sleep"]) + try: + # Login to Secondary storage VM to check snapshot present on sec disk + ssh_client = remoteSSHClient.remoteSSHClient( + self.services["mgmt_server"]["ipaddress"], + self.services["mgmt_server"]["port"], + self.services["mgmt_server"]["username"], + self.services["mgmt_server"]["password"], + ) + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s/%s %s" % ( + sec_storage_ip, + export_path, + self.services["mount_dir"] + ), + "ls %s/snapshots/%s/%s" % ( + self.services["mount_dir"], + account_id, + volume_id + ), + ] + + for c in cmds: + self.debug("command: %s" % c) + result = ssh_client.execute(c) + self.debug("Result: %s" % result) + + uuids.append(result) + + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + for c in cmds: + result = ssh_client.execute(c) + except Exception: + self.fail("SSH failed for management server: %s" % + self.services["mgmt_server"]["ipaddress"]) + + res = str(uuids) + self.assertEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + + self.debug("Deleting account: %s" % self.account.account.name) + # Delete account + self.account.delete(self.apiclient) + + interval = list_configurations( + self.apiclient, + name='account.cleanup.interval' + ) + self.assertEqual( + isinstance(interval, list), + True, + "Check list response returns a valid list" + ) + self.debug("account.cleanup.interval: %s" % interval[0].value) + + # Wait for account cleanup interval + time.sleep(int(interval[0].value) * 2) + + accounts = list_accounts( + self.apiclient, + id=self.account.account.id + ) + + self.assertEqual( + accounts, + None, + "List accounts should return empty list after account deletion" + ) + + uuids = [] + for host in hosts: + # hosts[0].name = "nfs://192.168.100.21/export/test" + parse_url = (host.name).split('/') + # parse_url = ['nfs:', '', '192.168.100.21', 'export', 'test'] + + # Split IP address and export path from name + sec_storage_ip = parse_url[2] + # Sec Storage IP: 192.168.100.21 + + export_path = '/'.join(parse_url[3:]) + # Export path: export/test + + try: + cmds = [ + "mount %s/%s %s" % ( + sec_storage_ip, + export_path, + self.services["mount_dir"] + ), + "ls %s/snapshots/%s/%s" % ( + self.services["mount_dir"], + account_id, + volume_id + ), + ] + + for c in cmds: + self.debug("command: %s" % c) + result = ssh_client.execute(c) + self.debug("Result: %s" % result) + + uuids.append(result) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + for c in cmds: + self.debug("command: %s" % c) + result = ssh_client.execute(c) + self.debug("Result: %s" % result) + + except Exception: + self.fail("SSH failed for management server: %s" % + self.services["mgmt_server"]["ipaddress"]) + + res = str(uuids) + self.assertNotEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + return + + +class TestSnapshotDetachedDisk(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestSnapshotDetachedDisk, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + cls.services["server"]["diskoffering"] = cls.disk_offering.id + + cls.services["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_03_snapshot_detachedDisk(self): + """Test snapshot from detached disk + """ + + # Validate the following + # 1. login in VM and write some data on data disk(use fdisk to + # partition datadisk,fdisk /dev/sdb, and make filesystem using + # mkfs.ext3) + # 2. Detach the data disk and write some data on data disk + # 3. perform the snapshot on the detached volume + # 4. listvolumes with VM id shouldn't show the detached volume + # 5. listSnapshots should list the snapshot that was created + # 6. verify that secondary storage NFS share contains the reqd volume + # under /secondary/snapshots/$accountid/$volumeid/$snapshot_uuid + # 7. verify backup_snap_id was non null in the `snapshots` table + + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check list response returns a valid list" + ) + volume = volumes[0] + random_data_0 = random_gen(100) + random_data_1 = random_gen(100) + try: + ssh_client = self.virtual_machine.get_ssh_client() + + #Format partition using ext3 + format_volume_to_ext3( + ssh_client, + self.services["diskdevice"] + ) + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s1 %s" % ( + self.services["diskdevice"], + self.services["mount_dir"] + ), + "pushd %s" % self.services["mount_dir"], + "mkdir -p %s/{%s,%s} " % ( + self.services["sub_dir"], + self.services["sub_lvl_dir1"], + self.services["sub_lvl_dir2"] + ), + "echo %s > %s/%s/%s" % ( + random_data_0, + self.services["sub_dir"], + self.services["sub_lvl_dir1"], + self.services["random_data"] + ), + "echo %s > %s/%s/%s" % ( + random_data_1, + self.services["sub_dir"], + self.services["sub_lvl_dir2"], + self.services["random_data"] + ), + "sync", + ] + for c in cmds: + self.debug(ssh_client.execute(c)) + + #detach volume from VM + cmd = detachVolume.detachVolumeCmd() + cmd.id = volume.id + self.apiclient.detachVolume(cmd) + + #Create snapshot from detached volume + snapshot = Snapshot.create(self.apiclient, volume.id) + self.cleanup.append(snapshot) + + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + + self.assertEqual( + volumes, + None, + "Check Volume is detached" + ) + + # Verify the snapshot was created or not + snapshots = list_snapshots( + self.apiclient, + id=snapshot.id + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list snapshots call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check snapshot id in list resources call" + ) + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.virtual_machine.ipaddress) + + # Fetch values from database + qresultset = self.dbclient.execute( + "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % snapshot.id + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = qresultset[0] + snapshot_uuid = qresult[0] # backup_snap_id = snapshot UUID + account_id = qresult[1] + volume_id = qresult[2] + + self.assertNotEqual( + str(qresult[0]), + 'NULL', + "Check if backup_snap_id is not null" + ) + + # Get the Secondary Storage details from list Hosts + hosts = list_hosts( + self.apiclient, + type='SecondaryStorage', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + uuids = [] + for host in hosts: + # hosts[0].name = "nfs://192.168.100.21/export/test" + parse_url = (host.name).split('/') + # parse_url = ['nfs:', '', '192.168.100.21', 'export', 'test'] + + # Split IP address and export path from name + sec_storage_ip = parse_url[2] + # Sec Storage IP: 192.168.100.21 + + export_path = '/'.join(parse_url[3:]) + # Export path: export/test + + # Sleep to ensure that snapshot is reflected in sec storage + time.sleep(self.services["sleep"]) + try: + # Login to Management server to check snapshot present on + # sec disk + ssh_client = remoteSSHClient.remoteSSHClient( + self.services["mgmt_server"]["ipaddress"], + self.services["mgmt_server"]["port"], + self.services["mgmt_server"]["username"], + self.services["mgmt_server"]["password"], + ) + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s/%s %s" % ( + sec_storage_ip, + export_path, + self.services["mount_dir"] + ), + "ls %s/snapshots/%s/%s" % ( + self.services["mount_dir"], + account_id, + volume_id + ), + ] + + for c in cmds: + result = ssh_client.execute(c) + + uuids.append(result) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + for c in cmds: + result = ssh_client.execute(c) + except Exception as e: + self.fail("SSH failed for management server: %s" % + self.services["mgmt_server"]["ipaddress"]) + + res = str(uuids) + self.assertEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + return + + +class TestSnapshotLimit(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestSnapshotLimit, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_04_snapshot_limit(self): + """Test snapshot limit in snapshot policies + """ + + # Validate the following + # 1. Perform hourly recurring snapshot on the root disk of VM and keep + # the maxsnapshots as 1 + # 2. listSnapshots should list the snapshot that was created + # snapshot folder in secondary storage should contain only one + # snapshot image(/secondary/snapshots/$accountid/$volumeid/) + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check list response returns a valid list" + ) + volume = volumes[0] + + # Create a snapshot policy + recurring_snapshot = SnapshotPolicy.create( + self.apiclient, + volume.id, + self.services["recurring_snapshot"] + ) + self.cleanup.append(recurring_snapshot) + + snapshot_policy = list_snapshot_policy( + self.apiclient, + id=recurring_snapshot.id, + volumeid=volume.id + ) + self.assertEqual( + isinstance(snapshot_policy, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + snapshot_policy, + None, + "Check if result exists in list item call" + ) + + self.assertEqual( + snapshot_policy[0].id, + recurring_snapshot.id, + "Check recurring snapshot id in list resources call" + ) + self.assertEqual( + snapshot_policy[0].maxsnaps, + self.services["recurring_snapshot"]["maxsnaps"], + "Check interval type in list resources call" + ) + # Sleep for (maxsnaps+1) hours to verify + # only maxsnaps snapshots are retained + time.sleep( + (self.services["recurring_snapshot"]["maxsnaps"]) * 3600 + ) + + # Verify the snapshot was created or not + snapshots = list_snapshots( + self.apiclient, + volumeid=volume.id, + intervaltype=\ + self.services["recurring_snapshot"]["intervaltype"], + snapshottype='RECURRING', + listall=True + ) + + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertEqual( + len(snapshots), + self.services["recurring_snapshot"]["maxsnaps"], + "Check maximum number of recurring snapshots retained" + ) + snapshot = snapshots[0] + # Sleep to ensure that snapshot is reflected in sec storage + time.sleep(self.services["sleep"]) + + # Fetch values from database + qresultset = self.dbclient.execute( + "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % snapshot.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DBQuery returns a valid list" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = qresultset[0] + snapshot_uuid = qresult[0] # backup_snap_id = snapshot UUID + account_id = qresult[1] + volume_id = qresult[2] + + # Get the Secondary Storage details from list Hosts + hosts = list_hosts( + self.apiclient, + type='SecondaryStorage', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + uuids = [] + for host in hosts: + # hosts[0].name = "nfs://192.168.100.21/export/test" + parse_url = (host.name).split('/') + # parse_url = ['nfs:', '', '192.168.100.21', 'export', 'test'] + + # Split IP address and export path from name + sec_storage_ip = parse_url[2] + # Sec Storage IP: 192.168.100.21 + + export_path = '/'.join(parse_url[3:]) + # Export path: export/test + try: + # Login to VM to check snapshot present on sec disk + ssh_client = remoteSSHClient.remoteSSHClient( + self.services["mgmt_server"]["ipaddress"], + self.services["mgmt_server"]["port"], + self.services["mgmt_server"]["username"], + self.services["mgmt_server"]["password"], + ) + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s/%s %s" % ( + sec_storage_ip, + export_path, + self.services["mount_dir"] + ), + "ls %s/snapshots/%s/%s" % ( + self.services["mount_dir"], + account_id, + volume_id + ), + ] + + for c in cmds: + result = ssh_client.execute(c) + + uuids.append(result) + + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + for c in cmds: + result = ssh_client.execute(c) + except Exception as e: + raise Exception( + "SSH access failed for management server: %s" % + self.services["mgmt_server"]["ipaddress"]) + + res = str(uuids) + self.assertEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + return + + +class TestSnapshotEvents(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestSnapshotEvents, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_05_snapshot_events(self): + """Test snapshot events + """ + + # Validate the following + # 1. Perform snapshot on the root disk of this VM and check the events/alerts. + # 2. delete the snapshots and check the events/alerts + # 3. listEvents() shows created/deleted snapshot events + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check list response returns a valid list" + ) + volume = volumes[0] + + # Create a snapshot from the ROOTDISK + snapshot = Snapshot.create(self.apiclient, volumes[0].id) + self.debug("Snapshot created with ID: %s" % snapshot.id) + + snapshots = list_snapshots( + self.apiclient, + id=snapshot.id + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list snapshots call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check snapshot id in list resources call" + ) + snapshot.delete(self.apiclient) + + # Sleep to ensure that snapshot is deleted properly + time.sleep(self.services["sleep"]) + events = list_events( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid, + type='SNAPSHOT.DELETE' + ) + self.assertEqual( + isinstance(events, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + events, + None, + "Check if event exists in list events call" + ) + self.assertIn( + events[0].state, + ['Completed', 'Scheduled'], + "Check events state in list events call" + ) + return diff --git a/test/integration/component/test_templates.py b/test/integration/component/test_templates.py new file mode 100644 index 00000000000..82d85441855 --- /dev/null +++ b/test/integration/component/test_templates.py @@ -0,0 +1,612 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Templates +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +import urllib +from random import random +#Import System modules +import time + + +class Services: + """Test Templates Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "virtual_machine": { + "displayname": "testVM", + "hypervisor": 'XenServer', + "protocol": 'TCP', + "ssh_port": 22, + "username": "root", + "password": "password", + "privateport": 22, + "publicport": 22, + }, + "volume": { + "diskname": "Test Volume", + }, + "templates": { + # Configs for different Template formats + # For Eg. raw image, zip etc + 0:{ + "displaytext": "Public Template", + "name": "Public template", + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "url": "http://download.cloud.com/releases/2.0.0/UbuntuServer-10-04-64bit.vhd.bz2", + "hypervisor": 'XenServer', + "format" : 'VHD', + "isfeatured": True, + "ispublic": True, + "isextractable": True, + }, + }, + "template": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "templatefilter": 'self', + }, + "templatefilter": 'self', + "destzoneid": 2, # For Copy template (Destination zone) + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', # Networking mode: Advanced, basic + } + + +@unittest.skip("Open questions") +class TestCreateTemplate(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestCreateTemplate, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + + cls._cleanup = [ + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestCreateTemplate, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_create_template(self): + """Test create public & private template + """ + + # Validate the following: + # 1. Upload a templates in raw img format. Create a Vm instances from + # raw img template. + # 2. Upload a templates in zip file format. Create a Vm instances from + # zip template. + # 3. Upload a templates in tar format.Create a Vm instances from tar + # template. + # 4. Upload a templates in tar gzip format.Create a Vm instances from + # tar gzip template. + # 5. Upload a templates in tar bzip format. Create a Vm instances from + # tar bzip template. + # 6. Verify VMs & Templates is up and in ready state + + for k, v in self.services["templates"].items(): + + # Register new template + template = Template.register( + self.apiclient, + v, + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug( + "Registered a template of format: %s with ID: %s" % ( + v["format"], + template.id + )) + # Wait for template to download + template.download(self.apiclient) + self.cleanup.append(template) + + # Wait for template status to be changed across + time.sleep(self.services["sleep"]) + timeout = self.services["timeout"] + while True: + list_template_response = list_templates( + self.apiclient, + templatefilter=\ + self.services["templatefilter"], + id=template.id, + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + if isinstance(list_template_response, list): + break + elif timeout == 0: + raise Exception("List template failed!") + + time.sleep(5) + timeout = timeout - 1 + #Verify template response to check whether template added successfully + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check for list template response return valid data" + ) + + self.assertNotEqual( + len(list_template_response), + 0, + "Check template available in List Templates" + ) + + template_response = list_template_response[0] + self.assertEqual( + template_response.isready, + True, + "Check display text of newly created template" + ) + + # Deploy new virtual machine using template + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + mode=self.services["mode"] + ) + self.debug("creating an instance with template ID: %s" % template.id) + vm_response = list_virtual_machines( + self.apiclient, + id=virtual_machine.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(vm_response, list), + True, + "Check for list VMs response after VM deployment" + ) + #Verify VM response to check whether VM deployment was successful + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM created from Template" + ) + return + + +class TestTemplates(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.services = Services().services + cls.api_client = super(TestTemplates, cls).getClsTestClient().getApiClient() + + # Get Zone, templates etc + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + + # create virtual machine + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + ) + #Stop virtual machine + cls.virtual_machine.stop(cls.api_client) + + timeout = cls.services["timeout"] + #Wait before server has be successfully stopped + time.sleep(cls.services["sleep"]) + + while True: + list_volume = list_volumes( + cls.api_client, + virtualmachineid=cls.virtual_machine.id, + type='ROOT', + listall=True + ) + if isinstance(list_volume, list): + break + elif timeout == 0: + raise Exception("List volumes failed.") + + time.sleep(5) + timeout = timeout -1 + + cls.volume = list_volume[0] + + #Create template from volume + cls.template = Template.create( + cls.api_client, + cls.services["template"], + cls.volume.id + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestTemplates, cls).getClsTestClient().getApiClient() + #Cleanup created resources such as templates and VMs + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_create_template_volume(self): + """Test Create template from volume + """ + + # Validate the following: + # 1. Deploy new VM using the template created from Volume + # 2. VM should be in Up and Running state + + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + ) + + self.debug("creating an instance with template ID: %s" % self.template.id) + self.cleanup.append(virtual_machine) + vm_response = list_virtual_machines( + self.apiclient, + id=virtual_machine.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + #Verify VM response to check whether VM deployment was successful + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM created from Template" + ) + return + + def test_02_copy_template(self): + """Test for copy template from one zone to another""" + + # Validate the following + # 1. copy template should be successful and + # secondary storage should contain new copied template. + + self.debug( + "Copying template from zone: %s to %s" % ( + self.template.id, + self.services["destzoneid"] + )) + cmd = copyTemplate.copyTemplateCmd() + cmd.id = self.template.id + cmd.destzoneid = self.services["destzoneid"] + cmd.sourcezoneid = self.zone.id + self.apiclient.copyTemplate(cmd) + + # Verify template is copied to another zone using ListTemplates + list_template_response = list_templates( + self.apiclient, + templatefilter=\ + self.services["templatefilter"], + id=self.template.id, + zoneid=self.services["destzoneid"] + ) + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check for list template response return valid list" + ) + + self.assertNotEqual( + len(list_template_response), + 0, + "Check template extracted in List Templates" + ) + + template_response = list_template_response[0] + self.assertEqual( + template_response.id, + self.template.id, + "Check ID of the downloaded template" + ) + self.assertEqual( + template_response.zoneid, + self.services["destzoneid"], + "Check zone ID of the copied template" + ) + + # Cleanup- Delete the copied template + cmd = deleteTemplate.deleteTemplateCmd() + cmd.id = self.template.id + cmd.zoneid = self.services["destzoneid"] + self.apiclient.deleteTemplate(cmd) + return + + def test_03_delete_template(self): + """Test Delete template + """ + + # Validate the following: + # 1. Create a template and verify it is shown in list templates response + # 2. Delete the created template and again verify list template response + + # Verify template response for updated attributes + list_template_response = list_templates( + self.apiclient, + templatefilter=\ + self.services["template"]["templatefilter"], + id=self.template.id, + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check for list template response return valid list" + ) + + self.assertNotEqual( + len(list_template_response), + 0, + "Check template available in List Templates" + ) + template_response = list_template_response[0] + + self.assertEqual( + template_response.id, + self.template.id, + "Check display text of updated template" + ) + + self.debug("Deleting template: %s" % self.template) + # Delete the template + self.template.delete(self.apiclient) + self.debug("Delete template: %s successful" % self.template) + + list_template_response = list_templates( + self.apiclient, + templatefilter=\ + self.services["template"]["templatefilter"], + id=self.template.id, + zoneid=self.zone.id + ) + self.assertEqual( + list_template_response, + None, + "Check template available in List Templates" + ) + return + + def test_04_template_from_snapshot(self): + """Create Template from snapshot + """ + + # Validate the following + # 2. Snapshot the Root disk + # 3. Create Template from snapshot + # 4. Deploy Virtual machine using this template + # 5. VM should be in running state + + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + volume = volumes[0] + + self.debug("Creating a snapshot from volume: %s" % volume.id) + #Create a snapshot of volume + snapshot = Snapshot.create( + self.apiclient, + volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Creating a template from snapshot: %s" % snapshot.id) + # Generate template from the snapshot + template = Template.create_from_snapshot( + self.apiclient, + snapshot, + self.services["template"] + ) + self.cleanup.append(template) + # Verify created template + templates = list_templates( + self.apiclient, + templatefilter=\ + self.services["template"]["templatefilter"], + id=template.id + ) + self.assertNotEqual( + templates, + None, + "Check if result exists in list item call" + ) + + self.assertEqual( + templates[0].id, + template.id, + "Check new template id in list resources call" + ) + self.debug("Deploying a VM from template: %s" % template.id) + # Deploy new virtual machine using template + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + ) + self.cleanup.append(virtual_machine) + + vm_response = list_virtual_machines( + self.apiclient, + id=virtual_machine.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(vm_response, list), + True, + "Check for list VM response return valid list" + ) + + #Verify VM response to check whether VM deployment was successful + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM created from Template" + ) + return diff --git a/test/integration/component/test_usage.py b/test/integration/component/test_usage.py new file mode 100644 index 00000000000..1e37abda194 --- /dev/null +++ b/test/integration/component/test_usage.py @@ -0,0 +1,1574 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Snapshots +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +import datetime + +class Services: + """Test Snapshots Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "volume": { + "diskname": "TestDiskServ", + }, + "server": { + "displayname": "TestVM", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "templates": { + "displaytext": 'Template', + "name": 'Template', + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + "templatefilter": 'self', + "url": "http://download.cloud.com/releases/2.0.0/UbuntuServer-10-04-64bit.qcow2.bz2" + }, + "iso": { + "displaytext": "Test ISO", + "name": "Test ISO", + "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + # Source URL where ISO is located + "isextractable": True, + "isfeatured": True, + "ispublic": True, + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + }, + "lbrule": { + "name": "SSH", + "alg": "roundrobin", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + }, + "natrule": { + "privateport": 22, + "publicport": 22, + "protocol": "TCP" + }, + "vpn_user": { + "username": "test", + "password": "test", + }, + "ostypeid": '144f66aa-7f74-4cfe-9799-80cc21439cb3', + # Cent OS 5.3 (64 bit) + "sleep": 60, + "timeout": 10, + "mode":'advanced' + } + + +class TestVmUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestVmUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create Account, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_vm_usage(self): + """Test Create/Destroy VM and verify usage calculation + """ + + # Validate the following + # 1. Create a VM. Verify usage_events table contains VM .create, + # VM.start , Network.offering.assign , Volume.create events + # 2. Stop the VM. Verify usage_events table contains + # network.offerings.remove ,VM .stop Events for the created account. + # 3. Destroy the VM after some time. Verify usage_events table contains + # VM.Destroy and volume .delete Event for the created account + # 4. Delete the account + + self.debug("Stopping the VM: %s" % self.virtual_machine.id) + # Stop the VM + self.virtual_machine.stop(self.apiclient) + + time.sleep(self.services["sleep"]) + # Destroy the VM + self.debug("Destroying the VM: %s" % self.virtual_machine.id) + self.virtual_machine.delete(self.apiclient) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + # Check if VM.CREATE, VM.DESTROY events present in usage_event table + self.assertEqual( + qresult.count('VM.START'), + 1, + "Check VM.START event in events table" + ) + + self.assertEqual( + qresult.count('NETWORK.OFFERING.ASSIGN'), + 1, + "Check NETWORK.OFFERING.ASSIGN in events table" + ) + self.assertEqual( + qresult.count('VM.CREATE'), + 1, + "Check VM.CREATE in list events" + ) + + self.assertEqual( + qresult.count('VOLUME.CREATE'), + 1, + "Check VOLUME.CREATE in events table" + ) + + self.assertEqual( + qresult.count('VM.STOP'), + 1, + "Check VM.STOP in events table" + ) + + self.assertEqual( + qresult.count('NETWORK.OFFERING.REMOVE'), + 1, + "Check NETWORK.OFFERING.REMOVE in list events" + ) + + self.assertEqual( + qresult.count('VM.DESTROY'), + 1, + "Check VM.DESTROY in events table" + ) + + self.assertEqual( + qresult.count('VOLUME.DELETE'), + 1, + "Check VOLUME.DELETE in events table" + ) + return + + +class TestPublicIPUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestPublicIPUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create VMs, Assign Public IP etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + + cls.public_ip = PublicIPAddress.create( + cls.api_client, + cls.virtual_machine.account, + cls.virtual_machine.zoneid, + cls.virtual_machine.domainid, + cls.services["server"] + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_public_ip_usage(self): + """Test Assign new IP and verify usage calculation + """ + + # Validate the following + # 1. Aquire a IP for the network of this account. Verify usage_event + # table has Acquire IP event for the IP for this account + # 2. Release one of the IP of this account. Verify usage_event table + # has IP.Release event for released IP for this account + # 3. Delete the newly created account + + self.debug("Deleting public IP: %s" % + self.public_ip.ipaddress.ipaddress) + + # Release one of the IP + self.public_ip.delete(self.apiclient) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + # Check if NET.IPASSIGN, NET.IPRELEASE events present in usage_event + # table + self.assertEqual( + qresult.count('NET.IPASSIGN') > 0, + True, + "Check NET.IPASSIGN event in events table" + ) + + self.assertEqual( + qresult.count('NET.IPRELEASE') > 0, + True, + "Check NET.IPRELEASE in events table" + ) + return + + +class TestVolumeUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestVolumeUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + cls.services["server"]["diskoffering"] = cls.disk_offering.id + cls.services["template"] = template.id + + # Create Account, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_volume_usage(self): + """Test Create/delete a volume and verify correct usage is recorded + """ + + # Validate the following + # 1. Volume.create event for both root and data disk is there for the + # created account in cloud.usage_event table + # 2. Stop the VM + # 3. Detach the data disk from this VM + # 4. Destroy the Data disk. Volume.delete event is generated for data + # disk of the destroyed VM + + # Stop VM + self.debug("Stopping VM with ID: %s" % self.virtual_machine.id) + self.virtual_machine.stop(self.apiclient) + + volume_response = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(volume_response, list), + True, + "Check for valid list volumes response" + ) + data_volume = volume_response[0] + + # Detach data Disk + self.debug("Detaching volume ID: %s VM with ID: %s" % ( + data_volume.id, + self.virtual_machine.id + )) + self.virtual_machine.detach_volume(self.apiclient, data_volume) + + # Delete Data disk + self.debug("Delete volume ID: %s" % data_volume.id) + cmd = deleteVolume.deleteVolumeCmd() + cmd.id = data_volume.id + self.apiclient.deleteVolume(cmd) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + # Check VOLUME.CREATE, VOLUME.DESTROY events in cloud.usage_event table + self.assertEqual( + qresult.count('VOLUME.CREATE'), + 2, + "Check VOLUME.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('VOLUME.DELETE'), + 1, + "Check VOLUME.DELETE in events table" + ) + return + + +class TestTemplateUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestTemplateUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["server"]["zoneid"] = cls.zone.id + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + #create virtual machine + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + + #Stop virtual machine + cls.virtual_machine.stop(cls.api_client) + + #Wait before server has be successfully stopped + time.sleep(30) + list_volume = list_volumes( + cls.api_client, + virtualmachineid=cls.virtual_machine.id, + type='ROOT', + listall=True + ) + if isinstance(list_volume, list): + cls.volume = list_volume[0] + else: + raise Exception("List Volumes failed!") + cls._cleanup = [ + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, templates + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_template_usage(self): + """Test Upload/ delete a template and verify correct usage is generated + for the template uploaded + """ + + # Validate the following + # 1. Create a account + # 2. Upload a template from this account. template.create event is + # recorded in cloud.usage_event table for this account + # 3. Delete the template. template.delete event is recorded in + # cloud.usage_event tables for this account + # 4. Destroy the account + + #Create template from Virtual machine and Volume ID + self.template = Template.create( + self.apiclient, + self.services["templates"], + self.volume.id + ) + self.debug("Created template with ID: %s" % self.template.id) + # Delete template + self.template.delete(self.apiclient) + self.debug("Deleted template with ID: %s" % self.template.id) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for TEMPLATE.CREATE, TEMPLATE.DELETE in cloud.usage_event table + self.assertEqual( + qresult.count('TEMPLATE.CREATE'), + 1, + "Check TEMPLATE.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('TEMPLATE.DELETE'), + 1, + "Check TEMPLATE.DELETE in events table" + ) + return + + +class TestISOUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestISOUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.services["server"]["zoneid"] = cls.zone.id + cls.services["iso"]["zoneid"] = cls.zone.id + # Create Account, ISO image etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls.iso = Iso.create( + cls.api_client, + cls.services["iso"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + try: + # Wait till ISO gets downloaded + cls.iso.download(cls.api_client) + except Exception as e: + raise Exception("%s: Failed to download ISO: %s" % ( + e, + cls.iso.id + )) + cls._cleanup = [ + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created ISO images + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_ISO_usage(self): + """Test Create/Delete a ISO and verify its usage is generated correctly + """ + + # Validate the following + # 1. Create a account + # 2. Upload a ISO from this account. ISO.create event is recorded in + # cloud.usage_event table for this account + # 3. Delete the ISO. ISO.delete event is recorded in cloud.usage_event + # tables for this account + # 4. Destroy the account + + # Delete the ISO + self.debug("Deleting ISO with ID: %s" % self.iso.id) + self.iso.delete(self.apiclient) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for ISO.CREATE, ISO.DELETE events in cloud.usage_event table + self.assertEqual( + qresult.count('ISO.CREATE'), + 1, + "Check ISO.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('ISO.DELETE'), + 1, + "Check ISO.DELETE in events table" + ) + return + + +class TestLBRuleUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestLBRuleUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create VMs, LB Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.public_ip_1 = PublicIPAddress.create( + cls.api_client, + cls.virtual_machine.account, + cls.virtual_machine.zoneid, + cls.virtual_machine.domainid, + cls.services["server"] + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, LB rules + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_lb_usage(self): + """Test Create/Delete a LB rule and verify correct usage is recorded + """ + + # Validate the following + # 1. Acquire a IP for this account. lb.rule.create event is registered + # for this account in cloud.usage_event table + # 2. Create a LB rule on the IP associated with this account + # 3. Delete the created LB rule from the account. lb.rule.delete event + # is registered for this account in cloud.usage_event table + # 4. Delete this account. + + self.debug( + "Creating load balancer rule for public IP: %s" % + self.public_ip_1.ipaddress.id) + #Create Load Balancer rule and assign VMs to rule + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + self.public_ip_1.ipaddress.id, + accountid=self.account.account.name + ) + # Delete LB Rule + self.debug("Deleting LB rule with ID: %s" % lb_rule.id) + lb_rule.delete(self.apiclient) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for LB.CREATE, LB.DELETE in cloud.usage_event table + self.assertEqual( + qresult.count('LB.CREATE'), + 1, + "Check LB.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('LB.DELETE'), + 1, + "Check LB.DELETE in events table" + ) + return + + +class TestSnapshotUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestSnapshotUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create Account, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_snapshot_usage(self): + """Test Create/Delete a manual snap shot and verify + correct usage is recorded + """ + + # Validate the following + # 1. Create snapshot of the root disk for this account.Snapshot.create + # event is there for the created account in cloud.usage_event table + # 2. Destroy the snapshot after some time. Snapshot.delete event is + # generated for the destroyed Snapshot + # 3. Delete the account + + # Get the Root disk of VM + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check if list volumes return a valid data" + ) + + volume = volumes[0] + + # Create a snapshot from the ROOTDISK + self.debug("Creating snapshot from volume: %s" % volumes[0].id) + snapshot = Snapshot.create(self.apiclient, volumes[0].id) + + # Delete snapshot Rule + self.debug("Deleting snapshot: %s" % snapshot.id) + snapshot.delete(self.apiclient) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check if database query returns a valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query Result: %s" % qresult) + + # Check for SNAPSHOT.CREATE, SNAPSHOT.DELETE events in cloud.usage_event + # table + self.assertEqual( + qresult.count('SNAPSHOT.CREATE'), + 1, + "Check SNAPSHOT.CREATE event in events table" + ) + + self.assertEqual( + qresult.count('SNAPSHOT.DELETE'), + 1, + "Check SNAPSHOT.DELETE in events table" + ) + return + + +class TestNatRuleUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestNatRuleUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.public_ip_1 = PublicIPAddress.create( + cls.api_client, + cls.virtual_machine.account, + cls.virtual_machine.zoneid, + cls.virtual_machine.domainid, + cls.services["server"] + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, NAT rules + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_nat_usage(self): + """Test Create/Delete a PF rule and verify correct usage is recorded + """ + + # Validate the following + # 1. Acquire a IP for this account + # 2. Create a PF rule on the IP associated with this account. + # NET.RULEADD event is registered for this account in + # cloud.usage_event table + # 3. Delete the created PF rule from the account. NET.RULEDelete event + # is registered for this account in cloud.usage_event table + # 4. Delete this account. + + self.debug("Creating NAT rule with public IP: %s" % + self.public_ip_1.ipaddress.id) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + self.public_ip_1.ipaddress.id + ) + + # Delete NAT Rule + self.debug("Deleting NAT rule: %s" % nat_rule.id) + nat_rule.delete(self.apiclient) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for NET.RULEADD, NET.RULEDELETE in cloud.usage_event table + self.assertEqual( + qresult.count('NET.RULEADD'), + 1, + "Check NET.RULEADD event in events table" + ) + + self.assertEqual( + qresult.count('NET.RULEDELETE'), + 1, + "Check NET.RULEDELETE in events table" + ) + return + + +class TestVpnUsage(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestVpnUsage, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + + # Create Service offerings, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.public_ip = PublicIPAddress.create( + cls.api_client, + cls.virtual_machine.account, + cls.virtual_machine.zoneid, + cls.virtual_machine.domainid, + cls.services["server"] + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, VPN users + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_vpn_usage(self): + """Test Create/Delete a VPN and verify correct usage is recorded + """ + + # Validate the following + # 1. Enable VPN for this IP. vpn.add.user event is registered for this + # account in cloud.usage_event table + # 2. Add user to this vpn + # 3. Delete user for this VPN. vpn.user.delete event is registered for + # this account in cloud.usage_event table + # 4. Delete this account. + + self.debug("Created VPN with public IP: %s" % + self.public_ip.ipaddress.id) + #Assign VPN to Public IP + vpn = Vpn.create( + self.apiclient, + self.public_ip.ipaddress.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.debug("Created VPN user for account: %s" % + self.account.account.name) + + vpnuser = VpnUser.create( + self.apiclient, + self.services["vpn_user"]["username"], + self.services["vpn_user"]["password"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + # Remove VPN user + self.debug("Deleting VPN user: %s" % vpnuser.id) + vpnuser.delete(self.apiclient) + + # Delete VPN access + self.debug("Deleting VPN: %s" % vpn.publicipid) + vpn.delete(self.apiclient) + + # Fetch account ID from account_uuid + self.debug("select id from account where uuid = '%s';" \ + % self.account.account.id) + + qresultset = self.dbclient.execute( + "select id from account where uuid = '%s';" \ + % self.account.account.id + ) + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + qresult = qresultset[0] + + account_id = qresult[0] + self.debug("select type from usage_event where account_id = '%s';" \ + % account_id) + + qresultset = self.dbclient.execute( + "select type from usage_event where account_id = '%s';" \ + % account_id + ) + + self.assertEqual( + isinstance(qresultset, list), + True, + "Check DB query result set for valid data" + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = str(qresultset) + self.debug("Query result: %s" % qresult) + + # Check for VPN user related events + self.assertEqual( + qresult.count('VPN.USER.ADD'), + 1, + "Check VPN.USER.ADD event in events table" + ) + + self.assertEqual( + qresult.count('VPN.USER.ADD'), + 1, + "Check VPN.USER.ADD in events table" + ) + return diff --git a/test/integration/component/test_volumes.py b/test/integration/component/test_volumes.py new file mode 100644 index 00000000000..33c6b1ce4c0 --- /dev/null +++ b/test/integration/component/test_volumes.py @@ -0,0 +1,1019 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" P1 tests for Volumes +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +#Import System modules +import os +import urllib +import time +import tempfile + + +class Services: + """Test Volume Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "volume": { + "diskname": "TestDiskServ", + "max": 6, + }, + "virtual_machine": { + "displayname": "testVM", + "hypervisor": 'XenServer', + "protocol": 'TCP', + "ssh_port": 22, + "username": "root", + "password": "password", + "privateport": 22, + "publicport": 22, + }, + "iso": # ISO settings for Attach/Detach ISO tests + { + "displaytext": "Test ISO", + "name": "testISO", + "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + # Source URL where ISO is located + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + }, + "sleep": 50, + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "mode": 'advanced', + } + + +class TestAttachVolume(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestAttachVolume, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account + ] + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def test_01_volume_attach(self): + """Test Attach volumes (max capacity) + """ + + # Validate the following + # 1. Deploy a vm and create 5 data disk + # 2. Attach all the created Volume to the vm. + # 3. Reboot the VM. VM should be successfully rebooted + # 4. Stop the VM. Stop VM should be successful + # 5. Start The VM. Start VM should be successful + + # Create 5 volumes and attach to VM + for i in range(self.services["volume"]["max"]): + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.debug("Created volume: %s for account: %s" % ( + volume.id, + self.account.account.name + )) + # Check List Volume response for newly created volume + list_volume_response = list_volumes( + self.apiclient, + id=volume.id + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + # Attach volume to VM + self.virtual_machine.attach_volume( + self.apiclient, + volume + ) + self.debug("Attach volume: %s to VM: %s" % ( + volume.id, + self.virtual_machine.id + )) + # Check all volumes attached to same VM + list_volume_response = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + self.assertEqual( + len(list_volume_response), + self.services["volume"]["max"], + "Check number of data volumes attached to VM" + ) + self.debug("Rebooting the VM: %s" % self.virtual_machine.id) + # Reboot VM + self.virtual_machine.reboot(self.apiclient) + + vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id, + ) + #Verify VM response to check whether VM deployment was successful + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM" + ) + + self.debug("Stopping the VM: %s" % self.virtual_machine.id) + # Stop VM + self.virtual_machine.stop(self.apiclient) + + vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id, + ) + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + + #Verify VM response to check whether VM deployment was successful + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Stopped', + "Check the state of VM" + ) + + self.debug("Starting the VM: %s" % self.virtual_machine.id) + # Start VM + self.virtual_machine.start(self.apiclient) + # Sleep to ensure that VM is in ready state + time.sleep(self.services["sleep"]) + + vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id, + ) + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + + #Verify VM response to check whether VM deployment was successful + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM" + ) + return + + def test_02_volume_attach_max(self): + """Test attach volumes (more than max) to an instance + """ + + # Validate the following + # 1. Attach one more data volume to VM (Already 5 attached) + # 2. Attach volume should fail + + # Create a volume and attach to VM + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.debug("Created volume: %s for account: %s" % ( + volume.id, + self.account.account.name + )) + # Check List Volume response for newly created volume + list_volume_response = list_volumes( + self.apiclient, + id=volume.id + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + # Attach volume to VM + with self.assertRaises(Exception): + self.debug("Trying to Attach volume: %s to VM: %s" % ( + volume.id, + self.virtual_machine.id + )) + self.virtual_machine.attach_volume( + self.apiclient, + volume + ) + return + + def tearDown(self): + #Clean up, terminate the created volumes + cleanup_resources(self.apiclient, self.cleanup) + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestAttachVolume, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + +class TestAttachDetachVolume(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestAttachDetachVolume, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account + ] + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + #Clean up, terminate the created volumes + cleanup_resources(self.apiclient, self.cleanup) + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestAttachDetachVolume, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def test_01_volume_attach_detach(self): + """Test Volume attach/detach to VM (5 data volumes) + """ + + # Validate the following + # 1. Deploy a vm and create 5 data disk + # 2. Attach all the created Volume to the vm. + # 3. Detach all the volumes attached. + # 4. Reboot the VM. VM should be successfully rebooted + # 5. Stop the VM. Stop VM should be successful + # 6. Start The VM. Start VM should be successful + + volumes = [] + # Create 5 volumes and attach to VM + for i in range(self.services["volume"]["max"]): + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.debug("Created volume: %s for account: %s" % ( + volume.id, + self.account.account.name + )) + self.cleanup.append(volume) + volumes.append(volume) + + # Check List Volume response for newly created volume + list_volume_response = list_volumes( + self.apiclient, + id=volume.id + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + self.debug("Attach volume: %s to VM: %s" % ( + volume.id, + self.virtual_machine.id + )) + # Attach volume to VM + self.virtual_machine.attach_volume( + self.apiclient, + volume + ) + + # Check all volumes attached to same VM + list_volume_response = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + self.assertEqual( + len(list_volume_response), + self.services["volume"]["max"], + "Check number of data volumes attached to VM" + ) + + # Detach all volumes from VM + for volume in volumes: + self.debug("Detach volume: %s to VM: %s" % ( + volume.id, + self.virtual_machine.id + )) + self.virtual_machine.detach_volume( + self.apiclient, + volume + ) + # Reboot VM + self.debug("Rebooting the VM: %s" % self.virtual_machine.id) + self.virtual_machine.reboot(self.apiclient) + # Sleep to ensure that VM is in ready state + time.sleep(self.services["sleep"]) + + vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id, + ) + #Verify VM response to check whether VM deployment was successful + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM" + ) + + # Stop VM + self.debug("Stopping the VM: %s" % self.virtual_machine.id) + self.virtual_machine.stop(self.apiclient) + # Sleep to ensure that VM is in ready state + time.sleep(self.services["sleep"]) + + vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id, + ) + #Verify VM response to check whether VM deployment was successful + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Stopped', + "Check the state of VM" + ) + + # Start VM + self.debug("Starting the VM: %s" % self.virtual_machine.id) + self.virtual_machine.start(self.apiclient) + # Sleep to ensure that VM is in ready state + time.sleep(self.services["sleep"]) + + vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id, + ) + #Verify VM response to check whether VM deployment was successful + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.state, + 'Running', + "Check the state of VM" + ) + return + + +class TestAttachVolumeISO(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestAttachVolumeISO, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["iso"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account + ] + + @classmethod + def tearDownClass(cls): + try: + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_volume_iso_attach(self): + """Test Volumes and ISO attach + """ + + # Validate the following + # 1. Create and attach 5 data volumes to VM + # 2. Create an ISO. Attach it to VM instance + # 3. Verify that attach ISO is successful + + # Create 5 volumes and attach to VM + for i in range(self.services["volume"]["max"]): + volume = Volume.create( + self.apiclient, + self.services["volume"], + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.debug("Created volume: %s for account: %s" % ( + volume.id, + self.account.account.name + )) + # Check List Volume response for newly created volume + list_volume_response = list_volumes( + self.apiclient, + id=volume.id + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + # Attach volume to VM + self.virtual_machine.attach_volume( + self.apiclient, + volume + ) + + # Check all volumes attached to same VM + list_volume_response = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + self.assertEqual( + len(list_volume_response), + self.services["volume"]["max"], + "Check number of data volumes attached to VM" + ) + # Create an ISO and attach it to VM + iso = Iso.create( + self.apiclient, + self.services["iso"], + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.debug("Created ISO with ID: %s for account: %s" % ( + iso.id, + self.account.account.name + )) + self.cleanup.append(iso) + try: + self.debug("Downloading ISO with ID: %s" % iso.id) + iso.download(self.apiclient) + except Exception as e: + self.fail("Exception while downloading ISO %s: %s"\ + % (iso.id, e)) + + #Attach ISO to virtual machine + self.debug("Attach ISO ID: %s to VM: %s" % ( + iso.id, + self.virtual_machine.id + )) + cmd = attachIso.attachIsoCmd() + cmd.id = iso.id + cmd.virtualmachineid = self.virtual_machine.id + self.apiclient.attachIso(cmd) + + # Verify ISO is attached to VM + vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id, + ) + #Verify VM response to check whether VM deployment was successful + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM response for valid list" + ) + + self.assertNotEqual( + len(vm_response), + 0, + "Check VMs available in List VMs response" + ) + vm = vm_response[0] + self.assertEqual( + vm.isoid, + iso.id, + "Check ISO is attached to VM or not" + ) + return + + +class TestVolumes(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestVolumes, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["virtual_machine"]["template"] = template.id + cls.services["virtual_machine"]["diskofferingid"] = cls.disk_offering.id + + # Create VMs, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + ) + + cls.volume = Volume.create( + cls.api_client, + cls.services["volume"], + zoneid=cls.zone.id, + account=cls.account.account.name, + domainid=cls.account.account.domainid, + diskofferingid=cls.disk_offering.id + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account + ] + + @classmethod + def tearDownClass(cls): + try: + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + #Clean up, terminate the created volumes + cleanup_resources(self.apiclient, self.cleanup) + return + + def test_01_attach_volume(self): + """Attach a created Volume to a Running VM + """ + # Validate the following + # 1. Create a data volume. + # 2. List Volumes should not have vmname and virtualmachineid fields in + # response before volume attach (to VM) + # 3. Attch volume to VM. Attach volume should be successful. + # 4. List Volumes should have vmname and virtualmachineid fields in + # response before volume attach (to VM) + + # Check the list volumes response for vmname and virtualmachineid + list_volume_response = list_volumes( + self.apiclient, + id=self.volume.id + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + + volume = list_volume_response[0] + + self.assertEqual( + volume.type, + 'DATADISK', + "Check volume type from list volume response" + ) + + self.assertEqual( + hasattr(volume, 'vmname'), + True, + "Check whether volume has vmname field" + ) + self.assertEqual( + hasattr(volume, 'virtualmachineid'), + True, + "Check whether volume has virtualmachineid field" + ) + + # Attach volume to VM + self.debug("Attach volume: %s to VM: %s" % ( + self.volume.id, + self.virtual_machine.id + )) + self.virtual_machine.attach_volume(self.apiclient, self.volume) + + # Check all volumes attached to same VM + list_volume_response = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + volume = list_volume_response[0] + self.assertEqual( + volume.vmname, + self.virtual_machine.name, + "Check virtual machine name in list volumes response" + ) + self.assertEqual( + volume.virtualmachineid, + self.virtual_machine.id, + "Check VM ID in list Volume response" + ) + return + + def test_02_detach_volume(self): + """Detach a Volume attached to a VM + """ + + # Validate the following + # 1. Data disk should be detached from instance + # 2. Listvolumes should not have vmname and virtualmachineid fields for + # that volume. + + self.debug("Detach volume: %s to VM: %s" % ( + self.volume.id, + self.virtual_machine.id + )) + self.virtual_machine.detach_volume(self.apiclient, self.volume) + + #Sleep to ensure the current state will reflected in other calls + time.sleep(self.services["sleep"]) + + list_volume_response = list_volumes( + self.apiclient, + id=self.volume.id + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list volumes response for valid list" + ) + + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + volume = list_volume_response[0] + self.assertEqual( + volume.virtualmachineid, + None, + "Check if volume state (detached) is reflected" + ) + + self.assertEqual( + volume.vmname, + None, + "Check if volume state (detached) is reflected" + ) + return + + def test_03_delete_detached_volume(self): + """Delete a Volume unattached to an VM + """ + # Validate the following + # 1. volume should be deleted successfully and listVolume should not + # contain the deleted volume details. + + self.debug("Deleting volume: %s" % self.volume.id) + cmd = deleteVolume.deleteVolumeCmd() + cmd.id = self.volume.id + self.apiclient.deleteVolume(cmd) + + #Sleep to ensure the current state will reflected in other calls + time.sleep(self.services["sleep"]) + + list_volume_response = list_volumes( + self.apiclient, + id=self.volume.id, + type='DATADISK' + ) + self.assertEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + return diff --git a/test/integration/lib/__init__.py b/test/integration/lib/__init__.py new file mode 100644 index 00000000000..d65b5e89b69 --- /dev/null +++ b/test/integration/lib/__init__.py @@ -0,0 +1,13 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 \ No newline at end of file diff --git a/test/integration/lib/base.py b/test/integration/lib/base.py new file mode 100644 index 00000000000..11f01d2a607 --- /dev/null +++ b/test/integration/lib/base.py @@ -0,0 +1,1862 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 + +""" Base class for all Cloudstack resources + -Virtual machine, Volume, Snapshot etc +""" + +import marvin +from utils import is_server_ssh_ready, random_gen +from marvin.cloudstackAPI import * +#Import System modules +import time +import hashlib +import base64 +import types + + +class Domain: + """ Domain Life Cycle """ + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, name=None, networkdomain=None, + parentdomainid=None): + """Creates an domain""" + + cmd = createDomain.createDomainCmd() + + if name: + cmd.name = "-".join([name, random_gen()]) + elif "name" in services: + cmd.name = "-".join([services["name"], random_gen()]) + + if networkdomain: + cmd.networkdomain = networkdomain + elif "networkdomain" in services: + cmd.networkdomain = services["networkdomain"] + + if parentdomainid: + cmd.parentdomainid = parentdomainid + elif "parentdomainid" in services: + cmd.parentdomainid = services["parentdomainid"] + + return Domain(apiclient.createDomain(cmd).__dict__) + + def delete(self, apiclient, cleanup=None): + """Delete an domain""" + cmd = deleteDomain.deleteDomainCmd() + cmd.id = self.id + if cleanup: + cmd.cleanup = cleanup + apiclient.deleteDomain(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists domains""" + cmd = listDomains.listDomainsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listDomains(cmd)) + + +class Account: + """ Account Life Cycle """ + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, admin=False, domainid=None): + """Creates an account""" + cmd = createAccount.createAccountCmd() + + #0 - User, 1 - Root Admin, 2 - Domain Admin + cmd.accounttype = 2 if (admin and domainid) else int(admin) + + cmd.email = services["email"] + cmd.firstname = services["firstname"] + cmd.lastname = services["lastname"] + + # Password Encoding + mdf = hashlib.md5() + mdf.update(services["password"]) + cmd.password = mdf.hexdigest() + cmd.username = "-".join([services["username"], random_gen()]) + + if domainid: + cmd.domainid = domainid + account = apiclient.createAccount(cmd) + + return Account(account.__dict__) + + def delete(self, apiclient): + """Delete an account""" + cmd = deleteAccount.deleteAccountCmd() + cmd.id = self.account.id + apiclient.deleteAccount(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists accounts and provides detailed account information for + listed accounts""" + + cmd = listAccounts.listAccountsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listAccounts(cmd)) + + +class User: + """ User Life Cycle """ + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, account, domainid): + cmd = createUser.createUserCmd() + """Creates an user""" + + cmd.account = account + cmd.domainid = domainid + cmd.email = services["email"] + cmd.firstname = services["firstname"] + cmd.lastname = services["lastname"] + + # Password Encoding + mdf = hashlib.md5() + mdf.update(services["password"]) + cmd.password = mdf.hexdigest() + cmd.username = "-".join([services["username"], random_gen()]) + user = apiclient.createUser(cmd) + + return User(user.__dict__) + + def delete(self, apiclient): + """Delete an account""" + cmd = deleteUser.deleteUserCmd() + cmd.id = self.id + apiclient.deleteUser(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists users and provides detailed account information for + listed users""" + + cmd = listUsers.listUsersCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listUsers(cmd)) + + +class VirtualMachine: + """Manage virtual machine lifecycle""" + + def __init__(self, items, services): + self.__dict__.update(items) + self.username = services["username"] + self.password = services["password"] + self.ssh_port = services["ssh_port"] + self.ssh_client = None + #extract out the ipaddress + self.ipaddress = self.nic[0].ipaddress + + @classmethod + def create(cls, apiclient, services, templateid=None, accountid=None, + domainid=None, networkids=None, serviceofferingid=None, + securitygroupids=None, projectid=None, mode='basic'): + """Create the instance""" + + cmd = deployVirtualMachine.deployVirtualMachineCmd() + + if serviceofferingid: + cmd.serviceofferingid = serviceofferingid + elif "serviceoffering" in services: + cmd.serviceofferingid = services["serviceoffering"] + + cmd.zoneid = services["zoneid"] + cmd.hypervisor = services["hypervisor"] + + if accountid: + cmd.account = accountid + elif "account" in services: + cmd.account = services["account"] + + if domainid: + cmd.domainid = domainid + elif "domainid" in services: + cmd.domainid = services["domainid"] + + if networkids: + cmd.networkids = networkids + elif "networkids" in services: + cmd.networkids = services["networkids"] + + if templateid: + cmd.templateid = templateid + elif "template" in services: + cmd.templateid = services["template"] + + if "diskoffering" in services: + cmd.diskofferingid = services["diskoffering"] + + if securitygroupids: + cmd.securitygroupids = [str(sg_id) for sg_id in securitygroupids] + + if "userdata" in services: + cmd.userdata = base64.b64encode(services["userdata"]) + + if projectid: + cmd.projectid = projectid + + virtual_machine = apiclient.deployVirtualMachine(cmd) + + # VM should be in Running state after deploy + timeout = 10 + while True: + vm_status = VirtualMachine.list( + apiclient, + id=virtual_machine.id + ) + if isinstance(vm_status, list): + if vm_status[0].state == 'Running': + break + elif timeout == 0: + raise Exception( + "TimeOutException: Failed to start VM (ID: %s)" % + virtual_machine.id) + + time.sleep(10) + timeout = timeout -1 + + if mode.lower() == 'advanced': + public_ip = PublicIPAddress.create( + apiclient, + virtual_machine.account, + virtual_machine.zoneid, + virtual_machine.domainid, + services + ) + nat_rule = NATRule.create( + apiclient, + virtual_machine, + services, + ipaddressid=public_ip.ipaddress.id + ) + virtual_machine.ssh_ip = nat_rule.ipaddress + virtual_machine.public_ip = nat_rule.ipaddress + else: + virtual_machine.ssh_ip = virtual_machine.nic[0].ipaddress + virtual_machine.public_ip = virtual_machine.nic[0].ipaddress + + return VirtualMachine(virtual_machine.__dict__, services) + + def start(self, apiclient): + """Start the instance""" + cmd = startVirtualMachine.startVirtualMachineCmd() + cmd.id = self.id + apiclient.startVirtualMachine(cmd) + + def stop(self, apiclient): + """Stop the instance""" + cmd = stopVirtualMachine.stopVirtualMachineCmd() + cmd.id = self.id + apiclient.stopVirtualMachine(cmd) + + def reboot(self, apiclient): + """Reboot the instance""" + cmd = rebootVirtualMachine.rebootVirtualMachineCmd() + cmd.id = self.id + apiclient.rebootVirtualMachine(cmd) + + def get_ssh_client(self, ipaddress=None, reconnect=False, port=None): + """Get SSH object of VM""" + + # If NAT Rules are not created while VM deployment in Advanced mode + # then, IP address must be passed + if ipaddress != None: + self.ssh_ip = ipaddress + if port: + self.ssh_port = port + + if reconnect: + self.ssh_client = is_server_ssh_ready( + self.ssh_ip, + self.ssh_port, + self.username, + self.password + ) + self.ssh_client = self.ssh_client or is_server_ssh_ready( + self.ssh_ip, + self.ssh_port, + self.username, + self.password + ) + return self.ssh_client + + def delete(self, apiclient): + """Destroy an Instance""" + cmd = destroyVirtualMachine.destroyVirtualMachineCmd() + cmd.id = self.id + apiclient.destroyVirtualMachine(cmd) + + def attach_volume(self, apiclient, volume): + """Attach volume to instance""" + cmd = attachVolume.attachVolumeCmd() + cmd.id = volume.id + cmd.virtualmachineid = self.id + return apiclient.attachVolume(cmd) + + def detach_volume(self, apiclient, volume): + """Detach volume to instance""" + cmd = detachVolume.detachVolumeCmd() + cmd.id = volume.id + return apiclient.detachVolume(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all VMs matching criteria""" + + cmd = listVirtualMachines.listVirtualMachinesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVirtualMachines(cmd)) + + +class Volume: + """Manage Volume Lifecycle + """ + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, zoneid=None, account=None, domainid=None, + diskofferingid=None, projectid=None): + """Create Volume""" + cmd = createVolume.createVolumeCmd() + cmd.name = services["diskname"] + + if diskofferingid: + cmd.diskofferingid = diskofferingid + elif "diskofferingid" in services: + cmd.diskofferingid = services["diskofferingid"] + + if zoneid: + cmd.zoneid = zoneid + elif "zoneid" in services: + cmd.zoneid = services["zoneid"] + + if account: + cmd.account = account + elif "account" in services: + cmd.account = services["account"] + + if domainid: + cmd.domainid = domainid + elif "domainid" in services: + cmd.domainid = services["domainid"] + + if projectid: + cmd.projectid = projectid + return Volume(apiclient.createVolume(cmd).__dict__) + + @classmethod + def create_custom_disk(cls, apiclient, services, account=None, domainid=None): + """Create Volume from Custom disk offering""" + cmd = createVolume.createVolumeCmd() + cmd.name = services["diskname"] + cmd.diskofferingid = services["customdiskofferingid"] + cmd.size = services["customdisksize"] + cmd.zoneid = services["zoneid"] + + if account: + cmd.account = account + else: + cmd.account = services["account"] + + if domainid: + cmd.domainid = domainid + else: + cmd.domainid = services["domainid"] + + return Volume(apiclient.createVolume(cmd).__dict__) + + @classmethod + def create_from_snapshot(cls, apiclient, snapshot_id, services, + account=None, domainid=None): + """Create Volume from snapshot""" + cmd = createVolume.createVolumeCmd() + cmd.name = "-".join([services["diskname"], random_gen()]) + cmd.snapshotid = snapshot_id + cmd.zoneid = services["zoneid"] + cmd.size = services["size"] + if account: + cmd.account = account + else: + cmd.account = services["account"] + if domainid: + cmd.domainid = domainid + else: + cmd.domainid = services["domainid"] + return Volume(apiclient.createVolume(cmd).__dict__) + + def delete(self, apiclient): + """Delete Volume""" + cmd = deleteVolume.deleteVolumeCmd() + cmd.id = self.id + apiclient.deleteVolume(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all volumes matching criteria""" + + cmd = listVolumes.listVolumesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVolumes(cmd)) + + +class Snapshot: + """Manage Snapshot Lifecycle + """ + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, volume_id, account=None, domainid=None, projectid=None): + """Create Snapshot""" + cmd = createSnapshot.createSnapshotCmd() + cmd.volumeid = volume_id + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + if projectid: + cmd.projectid = projectid + return Snapshot(apiclient.createSnapshot(cmd).__dict__) + + def delete(self, apiclient): + """Delete Snapshot""" + cmd = deleteSnapshot.deleteSnapshotCmd() + cmd.id = self.id + apiclient.deleteSnapshot(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all snapshots matching criteria""" + + cmd = listSnapshots.listSnapshotsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listSnapshots(cmd)) + + +class Template: + """Manage template life cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, volumeid=None, + account=None, domainid=None, projectid=None): + """Create template from Volume""" + #Create template from Virtual machine and Volume ID + cmd = createTemplate.createTemplateCmd() + cmd.displaytext = services["displaytext"] + cmd.name = "-".join([services["name"], random_gen()]) + cmd.ostypeid = services["ostypeid"] + + cmd.isfeatured = services["isfeatured"] if "isfeatured" in services else False + cmd.ispublic = services["ispublic"] if "ispublic" in services else False + cmd.isextractable = services["isextractable"] if "isextractable" in services else False + cmd.passwordenabled = services["passwordenabled"] if "passwordenabled" in services else False + + if volumeid: + cmd.volumeid = volumeid + + if account: + cmd.account = account + + if domainid: + cmd.domainid = domainid + + if projectid: + cmd.projectid = projectid + return Template(apiclient.createTemplate(cmd).__dict__) + + @classmethod + def register(cls, apiclient, services, zoneid=None, account=None, domainid=None): + """Create template from URL""" + + #Create template from Virtual machine and Volume ID + cmd = registerTemplate.registerTemplateCmd() + cmd.displaytext = services["displaytext"] + cmd.name = "-".join([services["name"], random_gen()]) + cmd.format = services["format"] + cmd.hypervisor = services["hypervisor"] + cmd.ostypeid = services["ostypeid"] + cmd.url = services["url"] + + if zoneid: + cmd.zoneid = zoneid + else: + cmd.zoneid = services["zoneid"] + + cmd.isfeatured = services["isfeatured"] if "isfeatured" in services else False + cmd.ispublic = services["ispublic"] if "ispublic" in services else False + cmd.isextractable = services["isextractable"] if "isextractable" in services else False + + if account: + cmd.account = account + + if domainid: + cmd.domainid = domainid + + # Register Template + template = apiclient.registerTemplate(cmd) + + if isinstance(template, list): + return Template(template[0].__dict__) + + @classmethod + def create_from_snapshot(cls, apiclient, snapshot, services, random_name=True): + """Create Template from snapshot""" + #Create template from Virtual machine and Snapshot ID + cmd = createTemplate.createTemplateCmd() + cmd.displaytext = services["displaytext"] + cmd.name = "-".join([ + services["name"], + random_gen() + ]) if random_name else services["name"] + cmd.ostypeid = services["ostypeid"] + cmd.snapshotid = snapshot.id + return Template(apiclient.createTemplate(cmd).__dict__) + + def delete(self, apiclient): + """Delete Template""" + + cmd = deleteTemplate.deleteTemplateCmd() + cmd.id = self.id + apiclient.deleteTemplate(cmd) + + def download(self, apiclient, timeout=5, interval=60): + """Download Template""" + #Sleep to ensure template is in proper state before download + time.sleep(interval) + + while True: + template_response = Template.list( + apiclient, + id=self.id, + zoneid=self.zoneid, + templatefilter='self' + ) + if isinstance(template_response, list): + + template = template_response[0] + # If template is ready, + # template.status = Download Complete + # Downloading - x% Downloaded + # Error - Any other string + if template.status == 'Download Complete': + break + + elif 'Downloaded' in template.status: + time.sleep(interval) + + elif 'Installing' not in template.status: + raise Exception("ErrorInDownload") + + elif timeout == 0: + break + + else: + time.sleep(interval) + timeout = timeout - 1 + return + + def updatePermissions(self, apiclient, **kwargs): + """Updates the template permissions""" + + cmd = updateTemplatePermissions.updateTemplatePermissionsCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.updateTemplatePermissions(cmd)) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all templates matching criteria""" + + cmd = listTemplates.listTemplatesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listTemplates(cmd)) + + +class Iso: + """Manage ISO life cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, account=None, domainid=None, + projectid=None): + """Create an ISO""" + #Create ISO from URL + cmd = registerIso.registerIsoCmd() + cmd.displaytext = services["displaytext"] + cmd.name = services["name"] + cmd.ostypeid = services["ostypeid"] + cmd.url = services["url"] + cmd.zoneid = services["zoneid"] + + if "isextractable" in services: + cmd.isextractable = services["isextractable"] + if "isfeatured" in services: + cmd.isfeatured = services["isfeatured"] + if "ispublic" in services: + cmd.ispublic = services["ispublic"] + + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + if projectid: + cmd.projectid = projectid + # Register ISO + iso = apiclient.registerIso(cmd) + + if iso: + return Iso(iso[0].__dict__) + + def delete(self, apiclient): + """Delete an ISO""" + cmd = deleteIso.deleteIsoCmd() + cmd.id = self.id + apiclient.deleteIso(cmd) + return + + def download(self, apiclient, timeout=5, interval=60): + """Download an ISO""" + #Ensuring ISO is successfully downloaded + while True: + time.sleep(interval) + + cmd = listIsos.listIsosCmd() + cmd.id = self.id + iso_response = apiclient.listIsos(cmd) + + if isinstance(iso_response, list): + response = iso_response[0] + # Again initialize timeout to avoid listISO failure + timeout = 5 + + # Check whether download is in progress(for Ex:10% Downloaded) + # or ISO is 'Successfully Installed' + if response.status == 'Successfully Installed': + return + elif 'Downloaded' not in response.status: + raise Exception("ErrorInDownload") + + elif timeout == 0: + raise Exception("TimeoutException") + else: + timeout = timeout - 1 + return + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists all available ISO files.""" + + cmd = listIsos.listIsosCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listIsos(cmd)) + + +class PublicIPAddress: + """Manage Public IP Addresses""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, accountid=None, zoneid=None, domainid=None, + services=None, networkid=None, projectid=None): + """Associate Public IP address""" + cmd = associateIpAddress.associateIpAddressCmd() + + if accountid: + cmd.account = accountid + + if zoneid: + cmd.zoneid = zoneid + elif "zoneid" in services: + services["zoneid"] + + if domainid: + cmd.domainid = domainid + elif "domainid" in services: + services["domainid"] + + if networkid: + cmd.networkid = networkid + + if projectid: + cmd.projectid = projectid + return PublicIPAddress(apiclient.associateIpAddress(cmd).__dict__) + + def delete(self, apiclient): + """Dissociate Public IP address""" + cmd = disassociateIpAddress.disassociateIpAddressCmd() + cmd.id = self.ipaddress.id + apiclient.disassociateIpAddress(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """List all Public IPs matching criteria""" + + cmd = listPublicIpAddresses.listPublicIpAddressesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listPublicIpAddresses(cmd)) + +class NATRule: + """Manage port forwarding rule""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, virtual_machine, services, ipaddressid=None, + projectid=None): + """Create Port forwarding rule""" + cmd = createPortForwardingRule.createPortForwardingRuleCmd() + + if ipaddressid: + cmd.ipaddressid = ipaddressid + elif "ipaddressid" in services: + cmd.ipaddressid = services["ipaddressid"] + + cmd.privateport = services["privateport"] + cmd.publicport = services["publicport"] + cmd.protocol = services["protocol"] + cmd.virtualmachineid = virtual_machine.id + + if projectid: + cmd.projectid = projectid + + return NATRule(apiclient.createPortForwardingRule(cmd).__dict__) + + def delete(self, apiclient): + """Delete port forwarding""" + cmd = deletePortForwardingRule.deletePortForwardingRuleCmd() + cmd.id = self.id + apiclient.deletePortForwardingRule(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """List all NAT rules matching criteria""" + + cmd = listPortForwardingRules.listPortForwardingRulesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listPortForwardingRules(cmd)) + + +class StaticNATRule: + """Manage Static NAT rule""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, ipaddressid=None): + """Creates static ip forwarding rule""" + + cmd = createIpForwardingRule.createIpForwardingRuleCmd() + cmd.protocol = services["protocol"] + cmd.startport = services["startport"] + + if "endport" in services: + cmd.endport = services["endport"] + + if "cidrlist" in services: + cmd.cidrlist = services["cidrlist"] + + if ipaddressid: + cmd.ipaddressid = ipaddressid + elif "ipaddressid" in services: + cmd.ipaddressid = services["ipaddressid"] + + return StaticNATRule(apiclient.createIpForwardingRule(cmd).__dict__) + + def delete(self, apiclient): + """Delete IP forwarding rule""" + cmd = deleteIpForwardingRule.deleteIpForwardingRuleCmd() + cmd.id = self.id + apiclient.deleteIpForwardingRule(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """List all IP forwarding rules matching criteria""" + + cmd = listIpForwardingRules.listIpForwardingRulesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listIpForwardingRules(cmd)) + + @classmethod + def enable(cls, apiclient, ipaddressid, virtualmachineid): + """Enables Static NAT rule""" + + cmd = enableStaticNat.enableStaticNatCmd() + cmd.ipaddressid = ipaddressid + cmd.virtualmachineid = virtualmachineid + apiclient.enableStaticNat(cmd) + return + + @classmethod + def disable(cls, apiclient, ipaddressid, virtualmachineid): + """Disables Static NAT rule""" + + cmd = disableStaticNat.disableStaticNatCmd() + cmd.ipaddressid = ipaddressid + apiclient.disableStaticNat(cmd) + return + + +class FireWallRule: + """Manage Firewall rule""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, ipaddressid, protocol, cidrlist=None, + startport=None, endport=None, projectid=None): + """Create Firewall Rule""" + cmd = createFirewallRule.createFirewallRuleCmd() + cmd.ipaddressid = ipaddressid + cmd.protocol = protocol + if cidrlist: + cmd.cidrlist = cidrlist + if startport: + cmd.startport = startport + if endport: + cmd.endport = endport + + if projectid: + cmd.projectid = projectid + + return FireWallRule(apiclient.createFirewallRule(cmd).__dict__) + + def delete(self, apiclient): + """Delete Firewall rule""" + cmd = deleteFirewallRule.deleteFirewallRuleCmd() + cmd.id = self.id + apiclient.deleteFirewallRule(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """List all Firewall Rules matching criteria""" + + cmd = listFirewallRules.listFirewallRulesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listFirewallRules(cmd)) + + +class ServiceOffering: + """Manage service offerings cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, domainid=None, **kwargs): + """Create Service offering""" + cmd = createServiceOffering.createServiceOfferingCmd() + cmd.cpunumber = services["cpunumber"] + cmd.cpuspeed = services["cpuspeed"] + cmd.displaytext = services["displaytext"] + cmd.memory = services["memory"] + cmd.name = services["name"] + + # Service Offering private to that domain + if domainid: + cmd.domainid = domainid + + [setattr(cmd, k, v) for k, v in kwargs.items()] + return ServiceOffering(apiclient.createServiceOffering(cmd).__dict__) + + def delete(self, apiclient): + """Delete Service offering""" + cmd = deleteServiceOffering.deleteServiceOfferingCmd() + cmd.id = self.id + apiclient.deleteServiceOffering(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists all available service offerings.""" + + cmd = listServiceOfferings.listServiceOfferingsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listServiceOfferings(cmd)) + +class DiskOffering: + """Manage disk offerings cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, custom=False, domainid=None): + """Create Disk offering""" + cmd = createDiskOffering.createDiskOfferingCmd() + cmd.displaytext = services["displaytext"] + cmd.name = services["name"] + if custom: + cmd.customized = True + else: + cmd.disksize = services["disksize"] + + if domainid: + cmd.domainid = domainid + + return DiskOffering(apiclient.createDiskOffering(cmd).__dict__) + + def delete(self, apiclient): + """Delete Disk offering""" + cmd = deleteDiskOffering.deleteDiskOfferingCmd() + cmd.id = self.id + apiclient.deleteDiskOffering(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists all available disk offerings.""" + + cmd = listDiskOfferings.listDiskOfferingsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listDiskOfferings(cmd)) + + +class NetworkOffering: + """Manage network offerings cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, **kwargs): + """Create network offering""" + + cmd = createNetworkOffering.createNetworkOfferingCmd() + cmd.displaytext = "-".join([services["displaytext"], random_gen()]) + cmd.name = "-".join([services["name"], random_gen()]) + cmd.guestiptype = services["guestiptype"] + cmd.supportedservices = services["supportedservices"] + cmd.traffictype = services["traffictype"] + + cmd.serviceProviderList = [] + for service, provider in services["serviceProviderList"].items(): + cmd.serviceProviderList.append({ + 'service' : service, + 'provider': provider + }) + + [setattr(cmd, k, v) for k, v in kwargs.items()] + + return NetworkOffering(apiclient.createNetworkOffering(cmd).__dict__) + + def delete(self, apiclient): + """Delete network offering""" + cmd = deleteNetworkOffering.deleteNetworkOfferingCmd() + cmd.id = self.id + apiclient.deleteNetworkOffering(cmd) + return + + def update(self, apiclient, **kwargs): + """Lists all available network offerings.""" + + cmd = updateNetworkOffering.updateNetworkOfferingCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.updateNetworkOffering(cmd)) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists all available network offerings.""" + + cmd = listNetworkOfferings.listNetworkOfferingsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listNetworkOfferings(cmd)) + + +class SnapshotPolicy: + """Manage snapshot policies""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, volumeid, services): + """Create Snapshot policy""" + cmd = createSnapshotPolicy.createSnapshotPolicyCmd() + cmd.intervaltype = services["intervaltype"] + cmd.maxsnaps = services["maxsnaps"] + cmd.schedule = services["schedule"] + cmd.timezone = services["timezone"] + cmd.volumeid = volumeid + return SnapshotPolicy(apiclient.createSnapshotPolicy(cmd).__dict__) + + def delete(self, apiclient): + """Delete Snapshot policy""" + cmd = deleteSnapshotPolicies.deleteSnapshotPoliciesCmd() + cmd.id = self.id + apiclient.deleteSnapshotPolicies(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists snapshot policies.""" + + cmd = listSnapshotPolicies.listSnapshotPoliciesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listSnapshotPolicies(cmd)) + + +class LoadBalancerRule: + """Manage Load Balancer rule""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, ipaddressid, accountid=None, + networkid=None, projectid=None): + """Create Load balancing Rule""" + + cmd = createLoadBalancerRule.createLoadBalancerRuleCmd() + cmd.publicipid = ipaddressid or services["ipaddressid"] + + if accountid: + cmd.account = accountid + elif "account" in services: + cmd.account = services["account"] + + cmd.name = services["name"] + cmd.algorithm = services["alg"] + cmd.privateport = services["privateport"] + cmd.publicport = services["publicport"] + + if "openfirewall" in services: + cmd.openfirewall = services["openfirewall"] + + if projectid: + cmd.projectid = projectid + + if networkid: + cmd.networkid = networkid + return LoadBalancerRule(apiclient.createLoadBalancerRule(cmd).__dict__) + + def delete(self, apiclient): + """Delete load balancing rule""" + cmd = deleteLoadBalancerRule.deleteLoadBalancerRuleCmd() + cmd.id = self.id + apiclient.deleteLoadBalancerRule(cmd) + return + + def assign(self, apiclient, vms): + """Assign virtual machines to load balancing rule""" + cmd = assignToLoadBalancerRule.assignToLoadBalancerRuleCmd() + cmd.id = self.id + cmd.virtualmachineids = [str(vm.id) for vm in vms] + apiclient.assignToLoadBalancerRule(cmd) + return + + def remove(self, apiclient, vms): + """Remove virtual machines from load balancing rule""" + cmd = removeFromLoadBalancerRule.removeFromLoadBalancerRuleCmd() + cmd.id = self.id + cmd.virtualmachineids = [str(vm.id) for vm in vms] + apiclient.removeFromLoadBalancerRule(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """List all Load balancing rules matching criteria""" + + cmd = listLoadBalancerRules.listLoadBalancerRulesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listLoadBalancerRules(cmd)) + + +class Cluster: + """Manage Cluster life cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, zoneid=None, podid=None): + """Create Cluster""" + cmd = addCluster.addClusterCmd() + cmd.clustertype = services["clustertype"] + cmd.hypervisor = services["hypervisor"] + + if zoneid: + cmd.zoneid = zoneid + else: + cmd.zoneid = services["zoneid"] + + if podid: + cmd.podid = podid + else: + cmd.podid = services["podid"] + + if "username" in services: + cmd.username = services["username"] + if "password" in services: + cmd.password = services["password"] + if "url" in services: + cmd.url = services["url"] + if "clustername" in services: + cmd.clustername = services["clustername"] + + return Cluster(apiclient.addCluster(cmd)[0].__dict__) + + def delete(self, apiclient): + """Delete Cluster""" + cmd = deleteCluster.deleteClusterCmd() + cmd.id = self.id + apiclient.deleteCluster(cmd) + return + + @classmethod + def list(cls, apiclient, **kwargs): + """List all Clusters matching criteria""" + + cmd = listClusters.listClustersCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listClusters(cmd)) + + +class Host: + """Manage Host life cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, cluster, services, zoneid=None, podid=None): + """Create Host in cluster""" + + cmd = addHost.addHostCmd() + cmd.hypervisor = services["hypervisor"] + cmd.url = services["url"] + cmd.clusterid = cluster.id + + if zoneid: + cmd.zoneid = zoneid + else: + cmd.zoneid = services["zoneid"] + + if podid: + cmd.podid = podid + else: + cmd.podid = services["podid"] + + if "clustertype" in services: + cmd.clustertype = services["clustertype"] + if "username" in services: + cmd.username = services["username"] + if "password" in services: + cmd.password = services["password"] + + # Add host + host = apiclient.addHost(cmd) + + if isinstance(host, list): + return Host(host[0].__dict__) + + def delete(self, apiclient): + """Delete Host""" + # Host must be in maintenance mode before deletion + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = self.id + apiclient.prepareHostForMaintenance(cmd) + time.sleep(30) + + cmd = deleteHost.deleteHostCmd() + cmd.id = self.id + apiclient.deleteHost(cmd) + return + + def enableMaintenance(self, apiclient): + """enables maintainance mode Host""" + + cmd = prepareHostForMaintenance.prepareHostForMaintenanceCmd() + cmd.id = self.id + return apiclient.prepareHostForMaintenance(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all Hosts matching criteria""" + + cmd = listHosts.listHostsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listHosts(cmd)) + + +class StoragePool: + """Manage Storage pools (Primary Storage)""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, clusterid=None, zoneid=None, podid=None): + """Create Storage pool (Primary Storage)""" + + cmd = createStoragePool.createStoragePoolCmd() + cmd.name = services["name"] + + if podid: + cmd.podid = podid + else: + cmd.podid = services["podid"] + + cmd.url = services["url"] + if clusterid: + cmd.clusterid = clusterid + elif "clusterid" in services: + cmd.clusterid = services["clusterid"] + + if zoneid: + cmd.zoneid = zoneid + else: + cmd.zoneid = services["zoneid"] + + return StoragePool(apiclient.createStoragePool(cmd).__dict__) + + def delete(self, apiclient): + """Delete Storage pool (Primary Storage)""" + + # Storage pool must be in maintenance mode before deletion + cmd = enableStorageMaintenance.enableStorageMaintenanceCmd() + cmd.id = self.id + apiclient.enableStorageMaintenance(cmd) + time.sleep(30) + cmd = deleteStoragePool.deleteStoragePoolCmd() + cmd.id = self.id + apiclient.deleteStoragePool(cmd) + return + + def enableMaintenance(self, apiclient): + """enables maintainance mode Storage pool""" + + cmd = enableStorageMaintenance.enableStorageMaintenanceCmd() + cmd.id = self.id + return apiclient.enableStorageMaintenance(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all storage pools matching criteria""" + + cmd = listStoragePools.listStoragePoolsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listStoragePools(cmd)) + + +class Network: + """Manage Network pools""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, accountid=None, domainid=None, + networkofferingid=None, projectid=None, zoneid=None): + """Create Network for account""" + cmd = createNetwork.createNetworkCmd() + cmd.name = services["name"] + cmd.displaytext = services["displaytext"] + + if networkofferingid: + cmd.networkofferingid = networkofferingid + elif "networkoffering" in services: + cmd.networkofferingid = services["networkoffering"] + + if zoneid: + cmd.zoneid = zoneid + elif "zoneid" in services: + cmd.zoneid = services["zoneid"] + + if "gateway" in services: + cmd.gateway = services["gateway"] + if "netmask" in services: + cmd.netmask = services["netmask"] + if "startip" in services: + cmd.startip = services["startip"] + if "endip" in services: + cmd.endip = services["endip"] + if "vlan" in services: + cmd.vlan = services["vlan"] + if "acltype" in services: + cmd.acltype = services["acltype"] + + if accountid: + cmd.account = accountid + if domainid: + cmd.domainid = domainid + if projectid: + cmd.projectid = projectid + + return Network(apiclient.createNetwork(cmd).__dict__) + + def delete(self, apiclient): + """Delete Account""" + + cmd = deleteNetwork.deleteNetworkCmd() + cmd.id = self.id + apiclient.deleteNetwork(cmd) + + def update(self, apiclient, **kwargs): + """Updates network with parameters passed""" + + cmd = updateNetwork.updateNetworkCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.updateNetwork(cmd)) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all Networks matching criteria""" + + cmd = listNetworks.listNetworksCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listNetworks(cmd)) + + +class Vpn: + """Manage VPN life cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, publicipid, account=None, domainid=None, + projectid=None): + """Create VPN for Public IP address""" + cmd = createRemoteAccessVpn.createRemoteAccessVpnCmd() + cmd.publicipid = publicipid + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + if projectid: + cmd.projectid = projectid + return Vpn(apiclient.createRemoteAccessVpn(cmd).__dict__) + + def delete(self, apiclient): + """Delete remote VPN access""" + + cmd = deleteRemoteAccessVpn.deleteRemoteAccessVpnCmd() + cmd.publicipid = self.publicipid + apiclient.deleteRemoteAccessVpn(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all VPN matching criteria""" + + cmd = listRemoteAccessVpns.listRemoteAccessVpnsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listRemoteAccessVpns(cmd)) + + +class VpnUser: + """Manage VPN user""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, username, password, account=None, domainid=None, + projectid=None): + """Create VPN user""" + cmd = addVpnUser.addVpnUserCmd() + cmd.username = username + cmd.password = password + + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + if projectid: + cmd.projectid = projectid + return VpnUser(apiclient.addVpnUser(cmd).__dict__) + + def delete(self, apiclient): + """Remove VPN user""" + + cmd = removeVpnUser.removeVpnUserCmd() + cmd.username = self.username + cmd.account = self.account + cmd.domainid = self.domainid + apiclient.removeVpnUser(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """List all VPN Users matching criteria""" + + cmd = listVpnUsers.listVpnUsersCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVpnUsers(cmd)) + + +class Zone: + """Manage Zone""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, domainid=None): + """Create zone""" + cmd = createZone.createZoneCmd() + cmd.dns1 = services["dns1"] + cmd.internaldns1 = services["internaldns1"] + cmd.name = services["name"] + cmd.networktype = services["networktype"] + + if "dns2" in services: + cmd.dns2 = services["dns2"] + if "internaldns2" in services: + cmd.internaldns2 = services["internaldns2"] + if domainid: + cmd.domainid = domainid + + return Zone(apiclient.createZone(cmd).__dict__) + + def delete(self, apiclient): + """Delete Zone""" + + cmd = deleteZone.deleteZoneCmd() + cmd.id = self.id + apiclient.deleteZone(cmd) + + def update(self, apiclient, **kwargs): + """Update the zone""" + + cmd = updateZone.updateZoneCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in kwargs.items()] + return apiclient.updateZone(cmd) + + + @classmethod + def list(cls, apiclient, **kwargs): + """List all Zones matching criteria""" + + cmd = listZones.listZonesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listZones(cmd)) + + +class Pod: + """Manage Pod""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services): + """Create Pod""" + cmd = createPod.createPodCmd() + cmd.gateway = services["gateway"] + cmd.netmask = services["netmask"] + cmd.name = services["name"] + cmd.startip = services["startip"] + cmd.endip = services["endip"] + cmd.zoneid = services["zoneid"] + + return Pod(apiclient.createPod(cmd).__dict__) + + def delete(self, apiclient): + """Delete Pod""" + + cmd = deletePod.deletePodCmd() + cmd.id = self.id + apiclient.deletePod(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + "Returns a default pod for specified zone" + + cmd = listPods.listPodsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return apiclient.listPods(cmd) + + +class PublicIpRange: + """Manage VlanIpRange""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services): + """Create VlanIpRange""" + + cmd = createVlanIpRange.createVlanIpRangeCmd() + cmd.gateway = services["gateway"] + cmd.netmask = services["netmask"] + cmd.forvirtualnetwork = services["forvirtualnetwork"] + cmd.startip = services["startip"] + cmd.endip = services["endip"] + cmd.zoneid = services["zoneid"] + cmd.podid = services["podid"] + cmd.vlan = services["vlan"] + + return PublicIpRange(apiclient.createVlanIpRange(cmd).__dict__) + + def delete(self, apiclient): + """Delete VlanIpRange""" + + cmd = deleteVlanIpRange.deleteVlanIpRangeCmd() + cmd.id = self.id + apiclient.deleteVlanIpRange(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists all VLAN IP ranges.""" + + cmd = listVlanIpRanges.listVlanIpRangesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVlanIpRanges(cmd)) + + +class SecondaryStorage: + """Manage Secondary storage""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services): + """Create Secondary Storage""" + cmd = addSecondaryStorage.addSecondaryStorageCmd() + + cmd.url = services["url"] + if "zoneid" in services: + cmd.zoneid = services["zoneid"] + return SecondaryStorage(apiclient.addSecondaryStorage(cmd).__dict__) + + def delete(self, apiclient): + """Delete Secondary Storage""" + + cmd = deleteHost.deleteHostCmd() + cmd.id = self.id + apiclient.deleteHost(cmd) + + +class PhysicalNetwork: + """Manage physical network storage""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, zoneid, domainid=None): + """Create physical network""" + cmd = createPhysicalNetwork.createPhysicalNetworkCmd() + + cmd.name = services["name"] + cmd.zoneid = zoneid + if domainid: + cmd.domainid = domainid + return PhysicalNetwork(apiclient.createPhysicalNetwork(cmd).__dict__) + + def delete(self, apiclient): + """Delete Physical Network""" + + cmd = deletePhysicalNetwork.deletePhysicalNetworkCmd() + cmd.id = self.id + apiclient.deletePhysicalNetwork(cmd) + + def update(self, apiclient, **kwargs): + """Update Physical network state""" + + cmd = updatePhysicalNetwork.updatePhysicalNetworkCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in kwargs.items()] + return apiclient.updatePhysicalNetwork(cmd) + + def addTrafficType(self, apiclient, type): + """Add Traffic type to Physical network""" + + cmd = addTrafficType.addTrafficTypeCmd() + cmd.physicalnetworkid = self.id + cmd.traffictype = type + return apiclient.addTrafficType(cmd) + +class SecurityGroup: + """Manage Security Groups""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, account=None, domainid=None, + description=None, projectid=None): + """Create security group""" + cmd = createSecurityGroup.createSecurityGroupCmd() + + cmd.name = services["name"] + if account: + cmd.account = account + if domainid: + cmd.domainid=domainid + if description: + cmd.description=description + if projectid: + cmd.projectid = projectid + + return SecurityGroup(apiclient.createSecurityGroup(cmd).__dict__) + + def delete(self, apiclient): + """Delete Security Group""" + + cmd = deleteSecurityGroup.deleteSecurityGroupCmd() + cmd.id = self.id + apiclient.deleteSecurityGroup(cmd) + + def authorize(self, apiclient, services, + account=None, domainid=None, projectid=None): + """Authorize Ingress Rule""" + + cmd=authorizeSecurityGroupIngress.authorizeSecurityGroupIngressCmd() + + if domainid: + cmd.domainid = domainid + if account: + cmd.account = account + + if projectid: + cmd.projectid = projectid + cmd.securitygroupid=self.id + cmd.protocol=services["protocol"] + + if services["protocol"] == 'ICMP': + cmd.icmptype = -1 + cmd.icmpcode = -1 + else: + cmd.startport = services["startport"] + cmd.endport = services["endport"] + + cmd.cidrlist = services["cidrlist"] + return (apiclient.authorizeSecurityGroupIngress(cmd).__dict__) + + def revoke(self, apiclient, id): + """Revoke ingress rule""" + + cmd=revokeSecurityGroupIngress.revokeSecurityGroupIngressCmd() + cmd.id=id + return apiclient.revokeSecurityGroupIngress(cmd) + + def authorizeEgress(self, apiclient, services, account=None, domainid=None, + projectid=None, user_secgrp_list = {}): + """Authorize Egress Rule""" + + cmd=authorizeSecurityGroupEgress.authorizeSecurityGroupEgressCmd() + + if domainid: + cmd.domainid = domainid + if account: + cmd.account = account + + if projectid: + cmd.projectid = projectid + cmd.securitygroupid=self.id + cmd.protocol=services["protocol"] + + if services["protocol"] == 'ICMP': + cmd.icmptype = -1 + cmd.icmpcode = -1 + else: + cmd.startport = services["startport"] + cmd.endport = services["endport"] + + cmd.cidrlist = services["cidrlist"] + + cmd.usersecuritygrouplist = [] + for account, group in user_secgrp_list.items(): + cmd.usersecuritygrouplist.append({ + 'account' : account, + 'group': group + }) + + return (apiclient.authorizeSecurityGroupEgress(cmd).__dict__) + + def revokeEgress(self, apiclient, id): + """Revoke Egress rule""" + + cmd=revokeSecurityGroupEgress.revokeSecurityGroupEgressCmd() + cmd.id=id + return apiclient.revokeSecurityGroupEgress(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists all security groups.""" + + cmd = listSecurityGroups.listSecurityGroupsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listSecurityGroups(cmd)) + +class Project: + """Manage Project life cycle""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, account=None, domainid=None): + """Create project""" + + cmd = createProject.createProjectCmd() + cmd.displaytext = services["displaytext"] + cmd.name = "-".join([services["name"], random_gen()]) + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + + return Project(apiclient.createProject(cmd).__dict__) + + def delete(self, apiclient): + """Delete Project""" + + cmd = deleteProject.deleteProjectCmd() + cmd.id = self.id + apiclient.deleteProject(cmd) + + def update(self, apiclient, **kwargs): + """Updates the project""" + + cmd = updateProject.updateProjectCmd() + cmd.id = self.id + [setattr(cmd, k, v) for k, v in kwargs.items()] + return apiclient.updateProject(cmd) + + def activate(self, apiclient): + """Activates the suspended project""" + + cmd = activateProject.activateProjectCmd() + cmd.id = self.id + return apiclient.activateProject(cmd) + + def suspend(self, apiclient): + """Suspend the active project""" + + cmd = suspendProject.suspendProjectCmd() + cmd.id = self.id + return apiclient.suspendProject(cmd) + + def addAccount(self, apiclient, account=None, email=None): + """Add account to project""" + + cmd = addAccountToProject.addAccountToProjectCmd() + cmd.projectid = self.id + if account: + cmd.account = account + if email: + cmd.email = email + return apiclient.addAccountToProject(cmd) + + def deleteAccount(self, apiclient, account): + """Delete account from project""" + + cmd = deleteAccountFromProject.deleteAccountFromProjectCmd() + cmd.projectid = self.id + cmd.account = account + return apiclient.deleteAccountFromProject(cmd) + + @classmethod + def listAccounts(cls, apiclient, **kwargs): + """Lists all accounts associated with projects.""" + + cmd = listProjectAccounts.listProjectAccountsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listProjectAccounts(cmd)) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists all projects.""" + + cmd = listProjects.listProjectsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listProjects(cmd)) + +class ProjectInvitation: + """Manage project invitations""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def update(cls, apiclient, projectid, accept, account=None, token=None): + """Updates the project invitation for that account""" + + cmd = updateProjectInvitation.updateProjectInvitationCmd() + cmd.projectid = projectid + cmd.accept = accept + if account: + cmd.account = account + if token: + cmd.token = token + + return (apiclient.updateProjectInvitation(cmd).__dict__) + + def delete(self, apiclient, id): + """Deletes the project invitation""" + + cmd = deleteProjectInvitation.deleteProjectInvitationCmd() + cmd.id = id + return apiclient.deleteProjectInvitation(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists project invitations""" + + cmd = listProjectInvitations.listProjectInvitationsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listProjectInvitations(cmd)) + + +class Configurations: + """Manage Configuration""" + + @classmethod + def update(cls, apiclient, name, value=None): + """Updates the specified configuration""" + + cmd = updateConfiguration.updateConfigurationCmd() + cmd.name = name + cmd.value = value + apiclient.updateConfiguration(cmd) + + @classmethod + def list(cls, apiclient, **kwargs): + """Lists configurations""" + + cmd = listConfigurations.listConfigurationsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listConfigurations(cmd)) diff --git a/test/integration/lib/common.py b/test/integration/lib/common.py new file mode 100644 index 00000000000..0c84c755b6f --- /dev/null +++ b/test/integration/lib/common.py @@ -0,0 +1,472 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +"""Common functions +""" + +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from utils import * +from base import * + +#Import System modules +import time + +def get_domain(apiclient, services=None): + "Returns a default domain" + + cmd = listDomains.listDomainsCmd() + if services: + if "domainid" in services: + cmd.id = services["domainid"] + + domains = apiclient.listDomains(cmd) + + if isinstance(domains, list): + return domains[0] + else: + raise Exception("Failed to find specified domain.") + +def get_zone(apiclient, services=None): + "Returns a default zone" + + cmd = listZones.listZonesCmd() + if services: + if "zoneid" in services: + cmd.id = services["zoneid"] + + zones = apiclient.listZones(cmd) + + if isinstance(zones, list): + return zones[0] + else: + raise Exception("Failed to find specified zone.") + +def get_pod(apiclient, zoneid, services=None): + "Returns a default pod for specified zone" + + cmd = listPods.listPodsCmd() + cmd.zoneid = zoneid + + if services: + if "podid" in services: + cmd.id = services["podid"] + + pods = apiclient.listPods(cmd) + + if isinstance(pods, list): + return pods[0] + else: + raise Exception("Exception: Failed to find specified pod.") + +def get_template(apiclient, zoneid, ostypeid=12, services=None): + "Returns a template" + + cmd = listTemplates.listTemplatesCmd() + cmd.templatefilter = 'featured' + cmd.zoneid = zoneid + + if services: + if "template" in services: + cmd.id = services["template"] + + list_templates = apiclient.listTemplates(cmd) + + for template in list_templates: + if template.ostypeid == ostypeid: + return template + + raise Exception("Exception: Failed to find template with OSTypeID: %s" % + ostypeid) + return + +def download_systemplates_sec_storage(server, services): + """Download System templates on sec storage""" + + try: + # Login to management server + ssh = remoteSSHClient.remoteSSHClient( + server["ipaddress"], + server["port"], + server["username"], + server["password"] + ) + except Exception as e: + raise Exception("SSH access failted for server with IP address: %s" % + server["ipaddess"]) + # Mount Secondary Storage on Management Server + cmds = [ + "mkdir -p %s" % services["mnt_dir"], + "mount -t nfs %s:/%s %s" % ( + services["sec_storage"], + services["path"], + services["mnt_dir"] + ), + "%s -m %s -u %s -h %s -F" % ( + services["command"], + services["mnt_dir"], + services["download_url"], + services["hypervisor"] + ) + ] + for c in cmds: + result = ssh.execute(c) + + res = str(result) + + # Unmount the Secondary storage + ssh.execute("umount %s" % (services["mnt_dir"])) + + if res.count("Successfully installed system VM template") == 1: + return + else: + raise Exception("Failed to download System Templates on Sec Storage") + return + +def wait_for_ssvms(apiclient, zoneid, podid, interval=60): + """After setup wait for SSVMs to come Up""" + + time.sleep(interval) + timeout = 40 + while True: + list_ssvm_response = list_ssvms( + apiclient, + systemvmtype='secondarystoragevm', + zoneid=zoneid, + podid=podid + ) + ssvm = list_ssvm_response[0] + if ssvm.state != 'Running': + # Sleep to ensure SSVMs are Up and Running + time.sleep(interval) + timeout = timeout - 1 + elif ssvm.state == 'Running': + break + elif timeout == 0: + raise Exception("SSVM failed to come up") + break + + timeout = 40 + while True: + list_ssvm_response = list_ssvms( + apiclient, + systemvmtype='consoleproxy', + zoneid=zoneid, + podid=podid + ) + cpvm = list_ssvm_response[0] + if cpvm.state != 'Running': + # Sleep to ensure SSVMs are Up and Running + time.sleep(interval) + timeout = timeout - 1 + elif cpvm.state == 'Running': + break + elif timeout == 0: + raise Exception("CPVM failed to come up") + break + return + +def download_builtin_templates(apiclient, zoneid, hypervisor, host, linklocalip, interval=60): + """After setup wait till builtin templates are downloaded""" + + # Change IPTABLES Rules + result = get_process_status( + host["ipaddress"], + host["port"], + host["username"], + host["password"], + linklocalip, + "iptables -P INPUT ACCEPT" + ) + time.sleep(interval) + # Find the BUILTIN Templates for given Zone, Hypervisor + list_template_response = list_templates( + apiclient, + hypervisor=hypervisor, + zoneid=zoneid, + templatefilter='self' + ) + + if not isinstance(list_template_response, list): + raise Exception("Failed to download BUILTIN templates") + + # Ensure all BUILTIN templates are downloaded + templateid = None + for template in list_template_response: + if template.templatetype == "BUILTIN": + templateid = template.id + + # Sleep to ensure that template is in downloading state after adding + # Sec storage + time.sleep(interval) + while True: + template_response = list_templates( + apiclient, + id=templateid, + zoneid=zoneid, + templatefilter='self' + ) + template = template_response[0] + # If template is ready, + # template.status = Download Complete + # Downloading - x% Downloaded + # Error - Any other string + if template.status == 'Download Complete': + break + + elif 'Downloaded' in template.status: + time.sleep(interval) + + elif 'Installing' not in template.status: + raise Exception("ErrorInDownload") + + return + +def update_resource_limit(apiclient, resourcetype, account=None, domainid=None, + max=None, projectid=None): + """Updates the resource limit to 'max' for given account""" + + cmd = updateResourceLimit.updateResourceLimitCmd() + cmd.resourcetype = resourcetype + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + if max: + cmd.max = max + if projectid: + cmd.projectid = projectid + apiclient.updateResourceLimit(cmd) + return + +def list_routers(apiclient, **kwargs): + """List all Routers matching criteria""" + + cmd = listRouters.listRoutersCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listRouters(cmd)) + +def list_zones(apiclient, **kwargs): + """List all Zones matching criteria""" + + cmd = listZones.listZonesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listZones(cmd)) + +def list_networks(apiclient, **kwargs): + """List all Networks matching criteria""" + + cmd = listNetworks.listNetworksCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listNetworks(cmd)) + +def list_clusters(apiclient, **kwargs): + """List all Clusters matching criteria""" + + cmd = listClusters.listClustersCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listClusters(cmd)) + +def list_ssvms(apiclient, **kwargs): + """List all SSVMs matching criteria""" + + cmd = listSystemVms.listSystemVmsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listSystemVms(cmd)) + +def list_storage_pools(apiclient, **kwargs): + """List all storage pools matching criteria""" + + cmd = listStoragePools.listStoragePoolsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listStoragePools(cmd)) + +def list_virtual_machines(apiclient, **kwargs): + """List all VMs matching criteria""" + + cmd = listVirtualMachines.listVirtualMachinesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVirtualMachines(cmd)) + +def list_hosts(apiclient, **kwargs): + """List all Hosts matching criteria""" + + cmd = listHosts.listHostsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listHosts(cmd)) + +def list_configurations(apiclient, **kwargs): + """List configuration with specified name""" + + cmd = listConfigurations.listConfigurationsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listConfigurations(cmd)) + +def list_publicIP(apiclient, **kwargs): + """List all Public IPs matching criteria""" + + cmd = listPublicIpAddresses.listPublicIpAddressesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listPublicIpAddresses(cmd)) + +def list_nat_rules(apiclient, **kwargs): + """List all NAT rules matching criteria""" + + cmd = listPortForwardingRules.listPortForwardingRulesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listPortForwardingRules(cmd)) + +def list_lb_rules(apiclient, **kwargs): + """List all Load balancing rules matching criteria""" + + cmd = listLoadBalancerRules.listLoadBalancerRulesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listLoadBalancerRules(cmd)) + +def list_lb_instances(apiclient, **kwargs): + """List all Load balancing instances matching criteria""" + + cmd = listLoadBalancerRuleInstances.listLoadBalancerRuleInstancesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listLoadBalancerRuleInstances(cmd)) + +def list_firewall_rules(apiclient, **kwargs): + """List all Firewall Rules matching criteria""" + + cmd = listFirewallRules.listFirewallRulesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listFirewallRules(cmd)) + +def list_volumes(apiclient, **kwargs): + """List all volumes matching criteria""" + + cmd = listVolumes.listVolumesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVolumes(cmd)) + +def list_isos(apiclient, **kwargs): + """Lists all available ISO files.""" + + cmd = listIsos.listIsosCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listIsos(cmd)) + +def list_snapshots(apiclient, **kwargs): + """List all snapshots matching criteria""" + + cmd = listSnapshots.listSnapshotsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listSnapshots(cmd)) + +def list_templates(apiclient, **kwargs): + """List all templates matching criteria""" + + cmd = listTemplates.listTemplatesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listTemplates(cmd)) + +def list_domains(apiclient, **kwargs): + """Lists domains""" + + cmd = listDomains.listDomainsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listDomains(cmd)) + +def list_accounts(apiclient, **kwargs): + """Lists accounts and provides detailed account information for + listed accounts""" + + cmd = listAccounts.listAccountsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listAccounts(cmd)) + +def list_users(apiclient, **kwargs): + """Lists users and provides detailed account information for + listed users""" + + cmd = listUsers.listUsersCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listUsers(cmd)) + +def list_snapshot_policy(apiclient, **kwargs): + """Lists snapshot policies.""" + + cmd = listSnapshotPolicies.listSnapshotPoliciesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listSnapshotPolicies(cmd)) + +def list_events(apiclient, **kwargs): + """Lists events""" + + cmd = listEvents.listEventsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listEvents(cmd)) + +def list_disk_offering(apiclient, **kwargs): + """Lists all available disk offerings.""" + + cmd = listDiskOfferings.listDiskOfferingsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listDiskOfferings(cmd)) + +def list_service_offering(apiclient, **kwargs): + """Lists all available service offerings.""" + + cmd = listServiceOfferings.listServiceOfferingsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listServiceOfferings(cmd)) + +def list_vlan_ipranges(apiclient, **kwargs): + """Lists all VLAN IP ranges.""" + + cmd = listVlanIpRanges.listVlanIpRangesCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVlanIpRanges(cmd)) + +def list_usage_records(apiclient, **kwargs): + """Lists usage records for accounts""" + + cmd = listUsageRecords.listUsageRecordsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listUsageRecords(cmd)) + +def list_nw_service_prividers(apiclient, **kwargs): + """Lists Network service providers""" + + cmd = listNetworkServiceProviders.listNetworkServiceProvidersCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listNetworkServiceProviders(cmd)) + +def list_virtual_router_elements(apiclient, **kwargs): + """Lists Virtual Router elements""" + + cmd = listVirtualRouterElements.listVirtualRouterElementsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listVirtualRouterElements(cmd)) + +def list_network_offerings(apiclient, **kwargs): + """Lists network offerings""" + + cmd = listNetworkOfferings.listNetworkOfferingsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listNetworkOfferings(cmd)) + +def list_resource_limits(apiclient, **kwargs): + """Lists resource limits""" + + cmd = listResourceLimits.listResourceLimitsCmd() + [setattr(cmd, k, v) for k, v in kwargs.items()] + return(apiclient.listResourceLimits(cmd)) \ No newline at end of file diff --git a/test/integration/lib/utils.py b/test/integration/lib/utils.py new file mode 100644 index 00000000000..3caee9b8ae4 --- /dev/null +++ b/test/integration/lib/utils.py @@ -0,0 +1,176 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +"""Utilities functions +""" + +import marvin +import time +from marvin.remoteSSHClient import remoteSSHClient +from marvin.cloudstackAPI import * +from marvin import cloudstackConnection +#from cloudstackConnection import cloudConnection +from marvin import configGenerator +import logging +import string +import random +import imaplib +import email +import datetime + +def restart_mgmt_server(server): + """Restarts the management server""" + + try: + # Get the SSH client + ssh = is_server_ssh_ready( + server["ipaddress"], + server["port"], + server["username"], + server["password"], + ) + result = ssh.execute("/etc/init.d/cloud-management restart") + res = str(result) + # Server Stop - OK + # Server Start - OK + if res.count("OK") != 2: + raise ("ErrorInReboot!") + except Exception as e: + raise e + return + +def fetch_latest_mail(services, from_mail): + """Fetch mail""" + + # Login to mail server to verify email + mail = imaplib.IMAP4_SSL(services["server"]) + mail.login( + services["email"], + services["password"] + ) + mail.list() + mail.select(services["folder"]) + date = (datetime.date.today() - datetime.timedelta(1)).strftime("%d-%b-%Y") + + result, data = mail.uid( + 'search', + None, + '(SENTSINCE {date} HEADER FROM "{mail}")'.format( + date=date, + mail=from_mail + ) + ) + # Return False if email is not present + if data == []: + return False + + latest_email_uid = data[0].split()[-1] + result, data = mail.uid('fetch', latest_email_uid, '(RFC822)') + raw_email = data[0][1] + email_message = email.message_from_string(raw_email) + result = get_first_text_block(email_message) + return result + +def get_first_text_block(email_message_instance): + """fetches first text block from the mail""" + maintype = email_message_instance.get_content_maintype() + if maintype == 'multipart': + for part in email_message_instance.get_payload(): + if part.get_content_maintype() == 'text': + return part.get_payload() + elif maintype == 'text': + return email_message_instance.get_payload() + +def random_gen(size=6, chars=string.ascii_uppercase + string.digits): + """Generate Random Strings of variable length""" + return ''.join(random.choice(chars) for x in range(size)) + +def cleanup_resources(api_client, resources): + """Delete resources""" + for obj in resources: + obj.delete(api_client) + +def is_server_ssh_ready(ipaddress, port, username, password, retries=50): + """Return ssh handle else wait till sshd is running""" + loop_cnt = retries + while True: + try: + ssh = remoteSSHClient.remoteSSHClient( + ipaddress, + port, + username, + password + ) + except Exception as e: + if loop_cnt == 0: + raise e + loop_cnt = loop_cnt - 1 + time.sleep(30) + else: + return ssh + + +def format_volume_to_ext3(ssh_client, device="/dev/sda"): + """Format attached storage to ext3 fs""" + cmds = [ + "echo -e 'n\np\n1\n\n\nw' | fdisk %s" % device, + "mkfs.ext3 %s1" % device, + ] + for c in cmds: + ssh_client.execute(c) + +def fetch_api_client(config_file='datacenterCfg'): + """Fetch the Cloudstack API Client""" + config = configGenerator.get_setup_config(config_file) + mgt = config.mgtSvr[0] + testClientLogger = logging.getLogger("testClient") + asyncTimeout = 3600 + return cloudstackAPIClient.CloudStackAPIClient( + cloudstackConnection.cloudConnection( + mgt.mgtSvrIp, + mgt.port, + mgt.apiKey, + mgt.securityKey, + asyncTimeout, + testClientLogger + ) + ) + +def get_process_status(hostip, port, username, password, linklocalip, process): + """Double hop and returns a process status""" + + #SSH to the machine + ssh = remoteSSHClient.remoteSSHClient( + hostip, + port, + username, + password + ) + ssh_command = "ssh -i ~/.ssh/id_rsa.cloud -ostricthostkeychecking=no " + ssh_command = ssh_command + "-oUserKnownHostsFile=/dev/null -p 3922 %s %s" \ + % (linklocalip, process) + + # Double hop into router + timeout = 5 + # Ensure the SSH login is successful + while True: + res = ssh.execute(ssh_command) + + if res[0] != "Host key verification failed.": + break + elif timeout == 0: + break + + time.sleep(5) + timeout = timeout - 1 + return res \ No newline at end of file diff --git a/test/integration/smoke/README b/test/integration/smoke/README new file mode 100644 index 00000000000..b4c5d82ac19 --- /dev/null +++ b/test/integration/smoke/README @@ -0,0 +1,41 @@ +Build Verification Testing (BVT) Cases +-------------------------------------- +These test cases are the core functionality tests that ensure the application is stable and can be tested thoroughly. +These BVT cases definitions are located at : https://docs.google.com/a/cloud.com/spreadsheet/ccc?key=0Ak8acbfxQG8ndEppOGZSLV9mUF9idjVkTkZkajhTZkE&invite=CPij0K0L + + +Guidelines +---------- +BVT test cases are being developed using Python's unittests2. Following are certain guidelines being followed + 1. Tests exercised for the same resource should ideally be present under a single suite or file. + + 2. Time-consuming operations that create new cloud resources like server creation, volume creation etc + should not necessarily be exercised per unit test. The resources can be shared by creating them at + the class-level using setUpClass and shared across all instances during a single run. + + 3. Certain tests pertaining to NAT, Firewall and Load Balancing warrant fresh resources per test. Hence a call should be + taken by the stakeholders regarding sharing resources. + + 4. Ensure that the tearDown/tearDownClass functions clean up all the resources created during the test run. + +For more information about unittests: http://docs.python.org/library/unittest.html + + +BVT Tests +---------- +The following files contain these BVT cases: + +1. test_vm_life_cycle.py - VM Life Cycle tests +2. test_volumes.py - Volumes related tests +3. test_snapshots.py - Snapshots related tests +4. test_disk_offerings.py - Disk Offerings related tests +5. test_service_offerings.py - Service Offerings related tests +6. test_hosts.py - Hosts and Clusters related tests +7. test_iso.py - ISO related tests +8. test_network.py - Network related tests +9. test_primary_storage.py - Primary storage related tests +10. test_secondary_storage.py - Secondary storage related tests +11. test_ssvm.py - SSVM & CPVM related tests +12. test_templates.py - Templates related tests +13. test_routers.py - Router related tests + diff --git a/test/integration/smoke/__init__.py b/test/integration/smoke/__init__.py new file mode 100644 index 00000000000..d65b5e89b69 --- /dev/null +++ b/test/integration/smoke/__init__.py @@ -0,0 +1,13 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 \ No newline at end of file diff --git a/test/integration/smoke/test_disk_offerings.py b/test/integration/smoke/test_disk_offerings.py new file mode 100644 index 00000000000..f38e73ba2a7 --- /dev/null +++ b/test/integration/smoke/test_disk_offerings.py @@ -0,0 +1,213 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Disk offerings""" + +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + +class Services: + """Test Disk offerings Services + """ + + def __init__(self): + self.services = { + "off": { + "name": "Disk offering", + "displaytext": "Disk offering", + "disksize": 1 # in GB + }, + } + +class TestCreateDiskOffering(cloudstackTestCase): + + def setUp(self): + self.services = Services().services + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_create_disk_offering(self): + """Test to create disk offering""" + + # Validate the following: + # 1. createDiskOfferings should return valid info for new offering + # 2. The Cloud Database contains the valid information + + disk_offering = DiskOffering.create( + self.apiclient, + self.services["off"] + ) + self.cleanup.append(disk_offering) + + self.debug("Created Disk offering with ID: %s" % disk_offering.id) + + list_disk_response = list_disk_offering( + self.apiclient, + id=disk_offering.id + ) + self.assertEqual( + isinstance(list_disk_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_disk_response), + 0, + "Check Disk offering is created" + ) + disk_response = list_disk_response[0] + + self.assertEqual( + disk_response.displaytext, + self.services["off"]["displaytext"], + "Check server id in createServiceOffering" + ) + self.assertEqual( + disk_response.name, + self.services["off"]["name"], + "Check name in createServiceOffering" + ) + return + + +class TestDiskOfferings(cloudstackTestCase): + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestDiskOfferings, cls).getClsTestClient().getApiClient() + cls.disk_offering_1 = DiskOffering.create( + cls.api_client, + cls.services["off"] + ) + cls.disk_offering_2 = DiskOffering.create( + cls.api_client, + cls.services["off"] + ) + cls._cleanup = [cls.disk_offering_1] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestDiskOfferings, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_02_edit_disk_offering(self): + """Test to update existing disk offering""" + + # Validate the following: + # 1. updateDiskOffering should return + # a valid information for newly created offering + + #Generate new name & displaytext from random data + random_displaytext = random_gen() + random_name = random_gen() + + self.debug("Updating Disk offering with ID: %s" % + self.disk_offering_1.id) + + cmd = updateDiskOffering.updateDiskOfferingCmd() + cmd.id = self.disk_offering_1.id + cmd.displaytext = random_displaytext + cmd.name = random_name + + self.apiclient.updateDiskOffering(cmd) + + list_disk_response = list_disk_offering( + self.apiclient, + id=self.disk_offering_1.id + ) + self.assertEqual( + isinstance(list_disk_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_disk_response), + 0, + "Check disk offering is updated" + ) + + disk_response = list_disk_response[0] + + self.assertEqual( + disk_response.displaytext, + random_displaytext, + "Check service displaytext in updateServiceOffering" + ) + self.assertEqual( + disk_response.name, + random_name, + "Check service name in updateServiceOffering" + ) + return + + def test_03_delete_disk_offering(self): + """Test to delete disk offering""" + + # Validate the following: + # 1. deleteDiskOffering should return + # a valid information for newly created offering + + self.disk_offering_2.delete(self.apiclient) + + self.debug("Deleted Disk offering with ID: %s" % + self.disk_offering_2.id) + list_disk_response = list_disk_offering( + self.apiclient, + id=self.disk_offering_2.id + ) + + self.assertEqual( + list_disk_response, + None, + "Check if disk offering exists in listDiskOfferings" + ) + return diff --git a/test/integration/smoke/test_hosts.py b/test/integration/smoke/test_hosts.py new file mode 100644 index 00000000000..2fafb76a50b --- /dev/null +++ b/test/integration/smoke/test_hosts.py @@ -0,0 +1,224 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Hosts and Clusters +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + +#Import System modules +import time + +class Services: + """Test Hosts & Clusters Services + """ + + def __init__(self): + self.services = { + "clusters": { + 0: { + "clustername": "Xen Cluster", + "clustertype": "CloudManaged", + # CloudManaged or ExternalManaged" + "hypervisor": "XenServer", + # Hypervisor type + }, + 1: { + "clustername": "KVM Cluster", + "clustertype": "CloudManaged", + # CloudManaged or ExternalManaged" + "hypervisor": "KVM", + # Hypervisor type + }, + 2: { + "hypervisor": 'VMware', + # Hypervisor type + "clustertype": 'ExternalManaged', + # CloudManaged or ExternalManaged" + "username": 'administrator', + "password": 'fr3sca', + "url": 'http://192.168.100.17/CloudStack-Clogeny-Pune/Pune-1', + # Format:http://vCenter Host/Datacenter/Cluster + "clustername": 'VMWare Cluster', + }, + }, + "hosts": { + "xenserver": { + # Must be name of corresponding Hypervisor type + # in cluster in small letters + "hypervisor": 'XenServer', + # Hypervisor type + "clustertype": 'CloudManaged', + # CloudManaged or ExternalManaged" + "url": 'http://192.168.100.211', + "username": "root", + "password": "fr3sca", + }, + "kvm": { + "hypervisor": 'KVM', + # Hypervisor type + "clustertype": 'CloudManaged', + # CloudManaged or ExternalManaged" + "url": 'http://192.168.100.212', + "username": "root", + "password": "fr3sca", + }, + "vmware": { + "hypervisor": 'VMware', + # Hypervisor type + "clustertype": 'ExternalManaged', + # CloudManaged or ExternalManaged" + "url": 'http://192.168.100.203', + "username": "administrator", + "password": "fr3sca", + }, + }, + } + +class TestHosts(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.services = Services().services + self.zone = get_zone(self.apiclient, self.services) + self.pod = get_pod(self.apiclient, self.zone.id, self.services) + self.cleanup = [] + + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_clusters(self): + """Test Add clusters & hosts - XEN, KVM, VWARE + """ + + # Validate the following: + # 1. Verify hypervisortype returned by API is Xen/KVM/VWare + # 2. Verify that the cluster is in 'Enabled' allocation state + # 3. Verify that the host is added successfully and in Up state + # with listHosts API response + + #Create clusters with Hypervisor type XEN/KVM/VWare + for k, v in self.services["clusters"].items(): + cluster = Cluster.create( + self.apiclient, + v, + zoneid=self.zone.id, + podid=self.pod.id + ) + self.debug( + "Created Cluster for hypervisor type %s & ID: %s" %( + v["hypervisor"], + cluster.id + )) + self.assertEqual( + cluster.hypervisortype, + v["hypervisor"], + "Check hypervisor type is " + v["hypervisor"] + " or not" + ) + self.assertEqual( + cluster.allocationstate, + 'Enabled', + "Check whether allocation state of cluster is enabled" + ) + + #If host is externally managed host is already added with cluster + response = list_hosts( + self.apiclient, + clusterid=cluster.id + ) + + if not response: + hypervisor_type = str(cluster.hypervisortype.lower()) + host = Host.create( + self.apiclient, + cluster, + self.services["hosts"][hypervisor_type], + zoneid=self.zone.id, + podid=self.pod.id + ) + self.debug( + "Created host (ID: %s) in cluster ID %s" %( + host.id, + cluster.id + )) + + #Cleanup Host & Cluster + self.cleanup.append(host) + self.cleanup.append(cluster) + + list_hosts_response = list_hosts( + self.apiclient, + clusterid=cluster.id + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check list Hosts response" + ) + + host_response = list_hosts_response[0] + #Check if host is Up and running + self.assertEqual( + host_response.state, + 'Up', + "Check if state of host is Up or not" + ) + #Verify List Cluster Response has newly added cluster + list_cluster_response = list_clusters( + self.apiclient, + id=cluster.id + ) + self.assertEqual( + isinstance(list_cluster_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_cluster_response), + 0, + "Check list Hosts response" + ) + + cluster_response = list_cluster_response[0] + self.assertEqual( + cluster_response.id, + cluster.id, + "Check cluster ID with list clusters response" + ) + self.assertEqual( + cluster_response.hypervisortype, + cluster.hypervisortype, + "Check hypervisor type with is " + v["hypervisor"] + " or not" + ) + return \ No newline at end of file diff --git a/test/integration/smoke/test_iso.py b/test/integration/smoke/test_iso.py new file mode 100644 index 00000000000..e1f8dfeb4b7 --- /dev/null +++ b/test/integration/smoke/test_iso.py @@ -0,0 +1,500 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Templates ISO +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +import urllib +from random import random +#Import System modules +import time + + +class Services: + """Test ISO Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "fr3sca", + }, + "iso_1": + { + "displaytext": "Test ISO 1", + "name": "ISO 1", + "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + # Source URL where ISO is located + "isextractable": True, + "isfeatured": True, + "ispublic": True, + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + }, + "iso_2": + { + "displaytext": "Test ISO 2", + "name": "ISO 2", + "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + # Source URL where ISO is located + "isextractable": True, + "isfeatured": True, + "ispublic": True, + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "mode": 'HTTP_DOWNLOAD', + # Used in Extract template, value must be HTTP_DOWNLOAD + }, + "destzoneid": 5, + # Copy ISO from one zone to another (Destination Zone) + "isfeatured": True, + "ispublic": True, + "isextractable": True, + "bootable": True, # For edit template + "passwordenabled": True, + "sleep": 60, + "timeout": 10, + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + # CentOS 5.3 (64 bit) + "mode": 'advanced' + # Networking mode: Basic or Advanced + } + + +class TestCreateIso(cloudstackTestCase): + + def setUp(self): + self.services = Services().services + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + # Get Zone, Domain and templates + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + self.services["domainid"] = self.domain.id + self.services["iso_2"]["zoneid"] = self.zone.id + + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=self.domain.id + ) + + self.cleanup = [self.account] + return + + def tearDown(self): + try: + + self.dbclient.close() + #Clean up, terminate the created ISOs + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_create_iso(self): + """Test create public & private ISO + """ + + # Validate the following: + # 1. database (vm_template table) should be + # updated with newly created ISO + # 2. UI should show the newly added ISO + # 3. listIsos API should show the newly added ISO + + iso = Iso.create( + self.apiclient, + self.services["iso_2"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("ISO created with ID: %s" % iso.id) + + try: + iso.download(self.apiclient) + except Exception as e: + self.fail("Exception while downloading ISO %s: %s"\ + % (iso.id, e)) + + list_iso_response = list_isos( + self.apiclient, + id=iso.id + ) + self.assertEqual( + isinstance(list_iso_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_iso_response), + 0, + "Check template available in List ISOs" + ) + iso_response = list_iso_response[0] + + self.assertEqual( + iso_response.displaytext, + self.services["iso_2"]["displaytext"], + "Check display text of newly created ISO" + ) + self.assertEqual( + iso_response.name, + self.services["iso_2"]["name"], + "Check name of newly created ISO" + ) + self.assertEqual( + iso_response.zoneid, + self.services["iso_2"]["zoneid"], + "Check zone ID of newly created ISO" + ) + return + + +class TestISO(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestISO, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + cls.services["domainid"] = cls.domain.id + cls.services["iso_1"]["zoneid"] = cls.zone.id + cls.services["iso_2"]["zoneid"] = cls.zone.id + cls.services["sourcezoneid"] = cls.zone.id + + #Create an account, ISOs etc. + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + cls.iso_1 = Iso.create( + cls.api_client, + cls.services["iso_1"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + try: + cls.iso_1.download(cls.api_client) + except Exception as e: + raise Exception("Exception while downloading ISO %s: %s"\ + % (cls.iso_1.id, e)) + + cls.iso_2 = Iso.create( + cls.api_client, + cls.services["iso_2"], + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + try: + cls.iso_2.download(cls.api_client) + except Exception as e: + raise Exception("Exception while downloading ISO %s: %s"\ + % (cls.iso_2.id, e)) + + cls._cleanup = [cls.account] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestISO, cls).getClsTestClient().getApiClient() + #Clean up, terminate the created templates + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created ISOs, VMs + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_02_edit_iso(self): + """Test Edit ISO + """ + + # Validate the following: + # 1. UI should show the edited values for ISO + # 2. database (vm_template table) should have updated values + + #Generate random values for updating ISO name and Display text + new_displayText = random_gen() + new_name = random_gen() + + self.debug("Updating ISO permissions for ISO: %s" % self.iso_1.id) + + cmd = updateIso.updateIsoCmd() + #Assign new values to attributes + cmd.id = self.iso_1.id + cmd.displaytext = new_displayText + cmd.name = new_name + cmd.bootable = self.services["bootable"] + cmd.passwordenabled = self.services["passwordenabled"] + + self.apiclient.updateIso(cmd) + + #Check whether attributes are updated in ISO using listIsos + list_iso_response = list_isos( + self.apiclient, + id=self.iso_1.id + ) + self.assertEqual( + isinstance(list_iso_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_iso_response), + 0, + "Check template available in List ISOs" + ) + + iso_response = list_iso_response[0] + self.assertEqual( + iso_response.displaytext, + new_displayText, + "Check display text of updated ISO" + ) + self.assertEqual( + iso_response.name, + new_name, + "Check name of updated ISO" + ) + self.assertEqual( + iso_response.bootable, + self.services["bootable"], + "Check if image is bootable of updated ISO" + ) + + self.assertEqual( + iso_response.ostypeid, + self.services["ostypeid"], + "Check OSTypeID of updated ISO" + ) + return + + def test_03_delete_iso(self): + """Test delete ISO + """ + + # Validate the following: + # 1. UI should not show the deleted ISP + # 2. database (vm_template table) should not contain deleted ISO + + self.debug("Deleting ISO with ID: %s" % self.iso_1.id) + self.iso_1.delete(self.apiclient) + + # Sleep to ensure that ISO state is reflected in other calls + time.sleep(self.services["sleep"]) + + #ListIsos to verify deleted ISO is properly deleted + list_iso_response = list_isos( + self.apiclient, + id=self.iso_1.id + ) + + self.assertEqual( + list_iso_response, + None, + "Check if ISO exists in ListIsos" + ) + return + + def test_04_extract_Iso(self): + "Test for extract ISO" + + # Validate the following + # 1. Admin should able extract and download the ISO + # 2. ListIsos should display all the public templates + # for all kind of users + # 3 .ListIsos should not display the system templates + + self.debug("Extracting ISO with ID: %s" % self.iso_2.id) + + cmd = extractIso.extractIsoCmd() + cmd.id = self.iso_2.id + cmd.mode = self.services["iso_2"]["mode"] + cmd.zoneid = self.services["iso_2"]["zoneid"] + list_extract_response = self.apiclient.extractIso(cmd) + + try: + #Format URL to ASCII to retrieve response code + formatted_url = urllib.unquote_plus(list_extract_response.url) + url_response = urllib.urlopen(formatted_url) + response_code = url_response.getcode() + except Exception: + self.fail( + "Extract ISO Failed with invalid URL %s (ISO id: %s)" \ + % (formatted_url, self.iso_2.id) + ) + + self.assertEqual( + list_extract_response.id, + self.iso_2.id, + "Check ID of the downloaded ISO" + ) + self.assertEqual( + list_extract_response.extractMode, + self.services["iso_2"]["mode"], + "Check mode of extraction" + ) + self.assertEqual( + list_extract_response.zoneid, + self.services["iso_2"]["zoneid"], + "Check zone ID of extraction" + ) + self.assertEqual( + response_code, + 200, + "Check for a valid response of download URL" + ) + return + + def test_05_iso_permissions(self): + """Update & Test for ISO permissions""" + + # validate the following + # 1. listIsos returns valid permissions set for ISO + # 2. permission changes should be reflected in vm_template + # table in database + + self.debug("Updating permissions for ISO: %s" % self.iso_2.id) + + cmd = updateIsoPermissions.updateIsoPermissionsCmd() + cmd.id = self.iso_2.id + #Update ISO permissions + cmd.isfeatured = self.services["isfeatured"] + cmd.ispublic = self.services["ispublic"] + cmd.isextractable = self.services["isextractable"] + self.apiclient.updateIsoPermissions(cmd) + + #Verify ListIsos have updated permissions for the ISO for normal user + list_iso_response = list_isos( + self.apiclient, + id=self.iso_2.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_iso_response, list), + True, + "Check list response returns a valid list" + ) + + iso_response = list_iso_response[0] + + self.assertEqual( + iso_response.id, + self.iso_2.id, + "Check ISO ID" + ) + self.assertEqual( + iso_response.ispublic, + self.services["ispublic"], + "Check ispublic permission of ISO" + ) + + self.assertEqual( + iso_response.isfeatured, + self.services["isfeatured"], + "Check isfeatured permission of ISO" + ) + return + + def test_06_copy_iso(self): + """Test for copy ISO from one zone to another""" + + #Validate the following + #1. copy ISO should be successful and secondary storage + # should contain new copied ISO. + + self.debug("Copy ISO from %s to %s" % ( + self.zone.id, + self.services["destzoneid"] + )) + + cmd = copyIso.copyIsoCmd() + cmd.id = self.iso_2.id + cmd.destzoneid = self.services["destzoneid"] + cmd.sourcezoneid = self.zone.id + self.apiclient.copyIso(cmd) + + #Verify ISO is copied to another zone using ListIsos + list_iso_response = list_isos( + self.apiclient, + id=self.iso_2.id, + zoneid=self.services["destzoneid"] + ) + self.assertEqual( + isinstance(list_iso_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_iso_response), + 0, + "Check template extracted in List ISO" + ) + iso_response = list_iso_response[0] + + self.assertEqual( + iso_response.id, + self.iso_2.id, + "Check ID of the downloaded ISO" + ) + self.assertEqual( + iso_response.zoneid, + self.services["destzoneid"], + "Check zone ID of the copied ISO" + ) + + self.debug("Cleanup copied ISO: %s" % iso_response.id) + # Cleanup- Delete the copied ISO + cmd = deleteIso.deleteIsoCmd() + cmd.id = iso_response.id + cmd.zoneid = self.services["destzoneid"] + self.apiclient.deleteIso(cmd) + return diff --git a/test/integration/smoke/test_network.py b/test/integration/smoke/test_network.py new file mode 100644 index 00000000000..7f629a6c6b0 --- /dev/null +++ b/test/integration/smoke/test_network.py @@ -0,0 +1,1712 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Network Life Cycle +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +#Import System modules +import time + + +class Services: + """Test Network Services + """ + + def __init__(self): + self.services = { + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + # Cent OS 5.3 (64 bit) + "mode": 'advanced', + # Networking mode: Basic or advanced + "lb_switch_wait": 10, + # Time interval after which LB switches the requests + "sleep": 60, + "timeout":10, + "network_offering": { + "name": 'Test Network offering', + "displaytext": 'Test Network offering', + "guestiptype": 'Isolated', + "supportedservices": 'Dhcp,Dns,SourceNat,PortForwarding', + "traffictype": 'GUEST', + "availability": 'Optional', + "serviceProviderList" : { + "Dhcp": 'VirtualRouter', + "Dns": 'VirtualRouter', + "SourceNat": 'VirtualRouter', + "PortForwarding": 'VirtualRouter', + }, + }, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, + # in MHz + "memory": 256, + # In MBs + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + "password": "password", + }, + "server": + { + "displayname": "Small Instance", + "username": "root", + "password": "password", + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "ssh_port": 22, + "protocol": 'TCP', + }, + "natrule": + { + "privateport": 22, + "publicport": 22, + "protocol": "TCP" + }, + "lbrule": + { + "name": "SSH", + "alg": "roundrobin", + # Algorithm used for load balancing + "privateport": 22, + "publicport": 2222, + } + } + + +class TestPublicIP(cloudstackTestCase): + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.services = Services().services + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestPublicIP, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + # Create Accounts & networks + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.user = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["network"]["zoneid"] = cls.zone.id + + cls.network_offering = NetworkOffering.create( + cls.api_client, + cls.services["network_offering"], + ) + # Enable Network offering + cls.network_offering.update(cls.api_client, state='Enabled') + + cls.services["network"]["networkoffering"] = cls.network_offering.id + cls.account_network = Network.create( + cls.api_client, + cls.services["network"], + cls.account.account.name, + cls.account.account.domainid + ) + cls.user_network = Network.create( + cls.api_client, + cls.services["network"], + cls.user.account.name, + cls.user.account.domainid + ) + + # Create Source NAT IP addresses + account_src_nat_ip = PublicIPAddress.create( + cls.api_client, + cls.account.account.name, + cls.zone.id, + cls.account.account.domainid + ) + user_src_nat_ip = PublicIPAddress.create( + cls.api_client, + cls.user.account.name, + cls.zone.id, + cls.user.account.domainid + ) + cls._cleanup = [ + cls.account_network, + cls.user_network, + cls.account, + cls.user, + cls.network_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_public_ip_admin_account(self): + """Test for Associate/Disassociate + public IP address for admin account""" + + # Validate the following: + # 1. listPubliIpAddresses API returns the list of acquired addresses + # 2. the returned list should contain our acquired IP address + + ip_address = PublicIPAddress.create( + self.apiclient, + self.account.account.name, + self.zone.id, + self.account.account.domainid + ) + list_pub_ip_addr_resp = list_publicIP( + self.apiclient, + id=ip_address.ipaddress.id + ) + self.assertEqual( + isinstance(list_pub_ip_addr_resp, list), + True, + "Check list response returns a valid list" + ) + #listPublicIpAddresses should return newly created public IP + self.assertNotEqual( + len(list_pub_ip_addr_resp), + 0, + "Check if new IP Address is associated" + ) + self.assertEqual( + list_pub_ip_addr_resp[0].id, + ip_address.ipaddress.id, + "Check Correct IP Address is returned in the List Cacls" + ) + + ip_address.delete(self.apiclient) + + # Validate the following: + # 1.listPublicIpAddresses should no more return the released address + list_pub_ip_addr_resp = list_publicIP( + self.apiclient, + id=ip_address.ipaddress.id + ) + self.assertEqual( + list_pub_ip_addr_resp, + None, + "Check if disassociated IP Address is no longer available" + ) + return + + def test_public_ip_user_account(self): + """Test for Associate/Disassociate + public IP address for user account""" + + # Validate the following: + # 1. listPubliIpAddresses API returns the list of acquired addresses + # 2. the returned list should contain our acquired IP address + + ip_address = PublicIPAddress.create( + self.apiclient, + self.user.account.name, + self.zone.id, + self.user.account.domainid + ) + + #listPublicIpAddresses should return newly created public IP + list_pub_ip_addr_resp = list_publicIP( + self.apiclient, + id=ip_address.ipaddress.id + ) + self.assertEqual( + isinstance(list_pub_ip_addr_resp, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_pub_ip_addr_resp), + 0, + "Check if new IP Address is associated" + ) + self.assertEqual( + list_pub_ip_addr_resp[0].id, + ip_address.ipaddress.id, + "Check Correct IP Address is returned in the List Call" + ) + + ip_address.delete(self.apiclient) + + list_pub_ip_addr_resp = list_publicIP( + self.apiclient, + id=ip_address.ipaddress.id + ) + + self.assertEqual( + list_pub_ip_addr_resp, + None, + "Check if disassociated IP Address is no longer available" + ) + return + + +class TestPortForwarding(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super(TestPortForwarding, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.services["server"]["zoneid"] = cls.zone.id + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls._cleanup = [ + cls.virtual_machine, + cls.account, + cls.service_offering + ] + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestPortForwarding, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def tearDown(self): + cleanup_resources(self.apiclient, self.cleanup) + return + + def test_01_port_fwd_on_src_nat(self): + """Test for port forwarding on source NAT""" + + #Validate the following: + #1. listPortForwarding rules API should return the added PF rule + #2. attempt to do an ssh into the user VM through the sourceNAT + + src_nat_ip_addrs = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(src_nat_ip_addrs, list), + True, + "Check list response returns a valid list" + ) + src_nat_ip_addr = src_nat_ip_addrs[0] + + # Check if VM is in Running state before creating NAT rule + vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id + ) + + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM returns a valid list" + ) + + self.assertNotEqual( + len(vm_response), + 0, + "Check Port Forwarding Rule is created" + ) + self.assertEqual( + vm_response[0].state, + 'Running', + "VM state should be Running before creating a NAT rule." + ) + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + src_nat_ip_addr.id + ) + + list_nat_rule_response = list_nat_rules( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(list_nat_rule_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_nat_rule_response), + 0, + "Check Port Forwarding Rule is created" + ) + self.assertEqual( + list_nat_rule_response[0].id, + nat_rule.id, + "Check Correct Port forwarding Rule is returned" + ) + #SSH virtual machine to test port forwarding + try: + self.debug("SSHing into VM with IP address %s with NAT IP %s" % + ( + self.virtual_machine.ipaddress, + src_nat_ip_addr.ipaddress + )) + + self.virtual_machine.get_ssh_client(src_nat_ip_addr.ipaddress) + + except Exception as e: + self.fail( + "SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + nat_rule.delete(self.apiclient) + + list_nat_rule_response = list_nat_rules( + self.apiclient, + id=nat_rule.id + ) + + self.assertEqual( + list_nat_rule_response, + None, + "Check Port Forwarding Rule is deleted" + ) + # Check if the Public SSH port is inaccessible + with self.assertRaises(Exception): + self.debug( + "SSHing into VM with IP address %s after NAT rule deletion" % + self.virtual_machine.ipaddress) + + remoteSSHClient.remoteSSHClient( + src_nat_ip_addr.ipaddress, + self.virtual_machine.ssh_port, + self.virtual_machine.username, + self.virtual_machine.password + ) + return + + def test_02_port_fwd_on_non_src_nat(self): + """Test for port forwarding on non source NAT""" + + #Validate the following: + #1. listPortForwardingRules should not return the deleted rule anymore + #2. attempt to do ssh should now fail + + ip_address = PublicIPAddress.create( + self.apiclient, + self.account.account.name, + self.zone.id, + self.account.account.domainid, + self.services["server"] + ) + self.cleanup.append(ip_address) + + # Check if VM is in Running state before creating NAT rule + vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine.id + ) + + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM returns a valid list" + ) + + self.assertNotEqual( + len(vm_response), + 0, + "Check Port Forwarding Rule is created" + ) + self.assertEqual( + vm_response[0].state, + 'Running', + "VM state should be Running before creating a NAT rule." + ) + + #Create NAT rule + nat_rule = NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + ip_address.ipaddress.id + ) + #Validate the following: + #1. listPortForwardingRules should not return the deleted rule anymore + #2. attempt to do ssh should now fail + + list_nat_rule_response = list_nat_rules( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + isinstance(list_nat_rule_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_nat_rule_response), + 0, + "Check Port Forwarding Rule is created" + ) + self.assertEqual( + list_nat_rule_response[0].id, + nat_rule.id, + "Check Correct Port forwarding Rule is returned" + ) + + try: + self.debug("SSHing into VM with IP address %s with NAT IP %s" % + ( + self.virtual_machine.ipaddress, + ip_address.ipaddress.ipaddress + )) + self.virtual_machine.get_ssh_client(ip_address.ipaddress.ipaddress) + except Exception as e: + self.fail( + "SSH Access failed for %s: %s" % \ + (self.virtual_machine.ipaddress, e) + ) + + nat_rule.delete(self.apiclient) + + list_nat_rule_response = list_nat_rules( + self.apiclient, + id=nat_rule.id + ) + self.assertEqual( + list_nat_rule_response, + None, + "Check Port Forwarding Rule is deleted" + ) + # Check if the Public SSH port is inaccessible + with self.assertRaises(Exception): + self.debug( + "SSHing into VM with IP address %s after NAT rule deletion" % + self.virtual_machine.ipaddress) + + remoteSSHClient.remoteSSHClient( + ip_address.ipaddress.ipaddress, + self.virtual_machine.ssh_port, + self.virtual_machine.username, + self.virtual_machine.password + ) + return + + +class TestLoadBalancingRule(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super(TestLoadBalancingRule, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["server"]["zoneid"] = cls.zone.id + + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.vm_1 = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.vm_2 = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.non_src_nat_ip = PublicIPAddress.create( + cls.api_client, + cls.account.account.name, + cls.zone.id, + cls.account.account.domainid, + cls.services["server"] + ) + cls._cleanup = [ + cls.account, + cls.service_offering + ] + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.cleanup = [] + return + + def tearDown(self): + cleanup_resources(self.apiclient, self.cleanup) + return + + @classmethod + def tearDownClass(cls): + cleanup_resources(cls.api_client, cls._cleanup) + return + + def test_01_create_lb_rule_src_nat(self): + """Test to create Load balancing rule with source NAT""" + + # Validate the Following: + #1. listLoadBalancerRules should return the added rule + #2. attempt to ssh twice on the load balanced IP + #3. verify using the hostname of the VM + # that round robin is indeed happening as expected + + src_nat_ip_addrs = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(src_nat_ip_addrs, list), + True, + "Check list response returns a valid list" + ) + src_nat_ip_addr = src_nat_ip_addrs[0] + + # Check if VM is in Running state before creating LB rule + vm_response = VirtualMachine.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM returns a valid list" + ) + + self.assertNotEqual( + len(vm_response), + 0, + "Check Port Forwarding Rule is created" + ) + for vm in vm_response: + self.assertEqual( + vm.state, + 'Running', + "VM state should be Running before creating a NAT rule." + ) + + #Create Load Balancer rule and assign VMs to rule + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + src_nat_ip_addr.id, + accountid=self.account.account.name + ) + self.cleanup.append(lb_rule) + + lb_rule.assign(self.apiclient, [self.vm_1, self.vm_2]) + + lb_rules = list_lb_rules( + self.apiclient, + id=lb_rule.id + ) + self.assertEqual( + isinstance(lb_rules, list), + True, + "Check list response returns a valid list" + ) + #verify listLoadBalancerRules lists the added load balancing rule + self.assertNotEqual( + len(lb_rules), + 0, + "Check Load Balancer Rule in its List" + ) + self.assertEqual( + lb_rules[0].id, + lb_rule.id, + "Check List Load Balancer Rules returns valid Rule" + ) + + # listLoadBalancerRuleInstances should list all + # instances associated with that LB rule + lb_instance_rules = list_lb_instances( + self.apiclient, + id=lb_rule.id + ) + self.assertEqual( + isinstance(lb_instance_rules, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(lb_instance_rules), + 0, + "Check Load Balancer instances Rule in its List" + ) + self.debug("lb_instance_rules Ids: %s, %s" % ( + lb_instance_rules[0].id, + lb_instance_rules[1].id + )) + self.debug("VM ids: %s, %s" % (self.vm_1.id, self.vm_2.id)) + + self.assertIn( + lb_instance_rules[0].id, + [self.vm_1.id, self.vm_2.id], + "Check List Load Balancer instances Rules returns valid VM ID" + ) + + self.assertIn( + lb_instance_rules[1].id, + [self.vm_1.id, self.vm_2.id], + "Check List Load Balancer instances Rules returns valid VM ID" + ) + try: + self.debug( + "SSH into VM (IPaddress: %s) & NAT Rule (Public IP: %s)"% + (self.vm_1.ipaddress, src_nat_ip_addr.ipaddress) + ) + + ssh_1 = remoteSSHClient.remoteSSHClient( + src_nat_ip_addr.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) + + # If Round Robin Algorithm is chosen, + # each ssh command should alternate between VMs + hostnames = [ssh_1.execute("hostname")[0]] + + except Exception as e: + self.fail("%s: SSH failed for VM with IP Address: %s" % + (e, src_nat_ip_addr.ipaddress)) + + time.sleep(self.services["lb_switch_wait"]) + + try: + self.debug("SSHing into IP address: %s after adding VMs (ID: %s , %s)" % + ( + src_nat_ip_addr.ipaddress, + self.vm_1.id, + self.vm_2.id + )) + + ssh_2 = remoteSSHClient.remoteSSHClient( + src_nat_ip_addr.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) + hostnames.append(ssh_2.execute("hostname")[0]) + + except Exception as e: + self.fail("%s: SSH failed for VM with IP Address: %s" % + (e, src_nat_ip_addr.ipaddress)) + + self.debug("Hostnames: %s" % str(hostnames)) + self.assertIn( + self.vm_1.name, + hostnames, + "Check if ssh succeeded for server1" + ) + self.assertIn( + self.vm_2.name, + hostnames, + "Check if ssh succeeded for server2" + ) + + #SSH should pass till there is a last VM associated with LB rule + lb_rule.remove(self.apiclient, [self.vm_2]) + try: + self.debug("SSHing into IP address: %s after removing VM (ID: %s)" % + ( + src_nat_ip_addr.ipaddress, + self.vm_2.id + )) + + ssh_1 = remoteSSHClient.remoteSSHClient( + src_nat_ip_addr.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) + + hostnames.append(ssh_1.execute("hostname")[0]) + + except Exception as e: + self.fail("%s: SSH failed for VM with IP Address: %s" % + (e, src_nat_ip_addr.ipaddress)) + + self.assertIn( + self.vm_1.name, + hostnames, + "Check if ssh succeeded for server1" + ) + + lb_rule.remove(self.apiclient, [self.vm_1]) + + with self.assertRaises(Exception): + self.debug("Removed all VMs, trying to SSH") + ssh_1 = remoteSSHClient.remoteSSHClient( + src_nat_ip_addr.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) + ssh_1.execute("hostname")[0] + return + + def test_02_create_lb_rule_non_nat(self): + """Test to create Load balancing rule with source NAT""" + + # Validate the Following: + #1. listLoadBalancerRules should return the added rule + #2. attempt to ssh twice on the load balanced IP + #3. verify using the hostname of the VM that + # round robin is indeed happening as expected + + # Check if VM is in Running state before creating LB rule + vm_response = VirtualMachine.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM returns a valid list" + ) + + self.assertNotEqual( + len(vm_response), + 0, + "Check Port Forwarding Rule is created" + ) + for vm in vm_response: + self.assertEqual( + vm.state, + 'Running', + "VM state should be Running before creating a NAT rule." + ) + + #Create Load Balancer rule and assign VMs to rule + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + self.non_src_nat_ip.ipaddress.id, + accountid=self.account.account.name + ) + self.cleanup.append(lb_rule) + + lb_rule.assign(self.apiclient, [self.vm_1, self.vm_2]) + + lb_rules = list_lb_rules( + self.apiclient, + id=lb_rule.id + ) + + self.assertEqual( + isinstance(lb_rules, list), + True, + "Check list response returns a valid list" + ) + #verify listLoadBalancerRules lists the added load balancing rule + self.assertNotEqual( + len(lb_rules), + 0, + "Check Load Balancer Rule in its List" + ) + self.assertEqual( + lb_rules[0].id, + lb_rule.id, + "Check List Load Balancer Rules returns valid Rule" + ) + # listLoadBalancerRuleInstances should list + # all instances associated with that LB rule + lb_instance_rules = list_lb_instances( + self.apiclient, + id=lb_rule.id + ) + self.assertEqual( + isinstance(lb_instance_rules, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(lb_instance_rules), + 0, + "Check Load Balancer instances Rule in its List" + ) + + self.assertIn( + lb_instance_rules[0].id, + [self.vm_1.id, self.vm_2.id], + "Check List Load Balancer instances Rules returns valid VM ID" + ) + + self.assertIn( + lb_instance_rules[1].id, + [self.vm_1.id, self.vm_2.id], + "Check List Load Balancer instances Rules returns valid VM ID" + ) + try: + self.debug("SSHing into IP address: %s after adding VMs (ID: %s , %s)" % + ( + self.non_src_nat_ip.ipaddress.ipaddress, + self.vm_1.id, + self.vm_2.id + )) + ssh_1 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) + + # If Round Robin Algorithm is chosen, + # each ssh command should alternate between VMs + hostnames = [ssh_1.execute("hostname")[0]] + + time.sleep(self.services["lb_switch_wait"]) + + self.debug("SSHing again into IP address: %s with VMs (ID: %s , %s) added to LB rule" % + ( + self.non_src_nat_ip.ipaddress.ipaddress, + self.vm_1.id, + self.vm_2.id + )) + ssh_2 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) + + hostnames.append(ssh_2.execute("hostname")[0]) + self.debug("Hostnames after adding 2 VMs to LB rule: %s" % str(hostnames)) + self.assertIn( + self.vm_1.name, + hostnames, + "Check if ssh succeeded for server1" + ) + self.assertIn( + self.vm_2.name, + hostnames, + "Check if ssh succeeded for server2" + ) + + #SSH should pass till there is a last VM associated with LB rule + lb_rule.remove(self.apiclient, [self.vm_2]) + + self.debug("SSHing into IP address: %s after removing VM (ID: %s) from LB rule" % + ( + self.non_src_nat_ip.ipaddress.ipaddress, + self.vm_2.id + )) + ssh_1 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) + + hostnames.append(ssh_1.execute("hostname")[0]) + self.debug("Hostnames after removing VM2: %s" % str(hostnames)) + except Exception as e: + self.fail("%s: SSH failed for VM with IP Address: %s" % + (e, self.non_src_nat_ip.ipaddress.ipaddress)) + + self.assertIn( + self.vm_1.name, + hostnames, + "Check if ssh succeeded for server1" + ) + + lb_rule.remove(self.apiclient, [self.vm_1]) + with self.assertRaises(Exception): + self.fail("SSHing into IP address: %s after removing VM (ID: %s) from LB rule" % + ( + self.non_src_nat_ip.ipaddress.ipaddress, + self.vm_1.id + )) + ssh_1 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress.ipaddress, + self.services['lbrule']["publicport"], + self.vm_1.username, + self.vm_1.password + ) + ssh_1.execute("hostname")[0] + return + + +class TestRebootRouter(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.services = Services().services + + # Get Zone, Domain and templates + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + template = get_template( + self.apiclient, + self.zone.id, + self.services["ostypeid"] + ) + self.services["server"]["zoneid"] = self.zone.id + + #Create an account, network, VM and IP addresses + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + self.vm_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + + src_nat_ip_addrs = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + try: + src_nat_ip_addr = src_nat_ip_addrs[0] + except Exception as e: + raise Exception("Warning: Exception during fetching source NAT: %s" % e) + + self.public_ip = PublicIPAddress.create( + self.apiclient, + self.vm_1.account, + self.vm_1.zoneid, + self.vm_1.domainid, + self.services["server"] + ) + + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + src_nat_ip_addr.id, + self.account.account.name + ) + lb_rule.assign(self.apiclient, [self.vm_1]) + self.nat_rule = NATRule.create( + self.apiclient, + self.vm_1, + self.services["natrule"], + ipaddressid=self.public_ip.ipaddress.id + ) + self.cleanup = [ + self.vm_1, + lb_rule, + self.service_offering, + self.nat_rule, + self.account, + ] + return + + def test_reboot_router(self): + """Test for reboot router""" + + #Validate the Following + #1. Post restart PF and LB rules should still function + #2. verify if the ssh into the virtual machine + # still works through the sourceNAT Ip + + #Retrieve router for the user account + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(routers, list), + True, + "Check list routers returns a valid list" + ) + + router = routers[0] + + self.debug("Rebooting the router (ID: %s)" % router.id) + + cmd = rebootRouter.rebootRouterCmd() + cmd.id = router.id + self.apiclient.rebootRouter(cmd) + + # Poll listVM to ensure VM is stopped properly + timeout = self.services["timeout"] + + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.vm_1.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Running': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to start VM (ID: %s) in change service offering" % vm.id) + + timeout = timeout - 1 + + #we should be able to SSH after successful reboot + try: + self.debug("SSH into VM (ID : %s ) after reboot" % self.vm_1.id) + + remoteSSHClient.remoteSSHClient( + self.nat_rule.ipaddress, + self.services["natrule"]["publicport"], + self.vm_1.username, + self.vm_1.password + ) + except Exception as e: + self.fail( + "SSH Access failed for %s: %s" % \ + (self.vm_1.ipaddress, e) + ) + return + + def tearDown(self): + cleanup_resources(self.apiclient, self.cleanup) + return + + +class TestAssignRemoveLB(cloudstackTestCase): + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.services = Services().services + # Get Zone, Domain and templates + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + template = get_template( + self.apiclient, + self.zone.id, + self.services["ostypeid"] + ) + self.services["server"]["zoneid"] = self.zone.id + + #Create VMs, accounts + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + + self.vm_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + + self.vm_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + + self.vm_3 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + + self.cleanup = [ + self.account, + self.service_offering + ] + return + + def test_assign_and_removal_elb(self): + """Test for assign & removing load balancing rule""" + + # Validate: + #1. Verify list API - listLoadBalancerRules lists + # all the rules with the relevant ports + #2. listLoadBalancerInstances will list + # the instances associated with the corresponding rule. + #3. verify ssh attempts should pass as long as there + # is at least one instance associated with the rule + + src_nat_ip_addrs = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(src_nat_ip_addrs, list), + True, + "Check list response returns a valid list" + ) + self.non_src_nat_ip = src_nat_ip_addrs[0] + + # Check if VM is in Running state before creating LB rule + vm_response = VirtualMachine.list( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.assertEqual( + isinstance(vm_response, list), + True, + "Check list VM returns a valid list" + ) + + self.assertNotEqual( + len(vm_response), + 0, + "Check Port Forwarding Rule is created" + ) + for vm in vm_response: + self.assertEqual( + vm.state, + 'Running', + "VM state should be Running before creating a NAT rule." + ) + + lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + self.non_src_nat_ip.id, + self.account.account.name + ) + lb_rule.assign(self.apiclient, [self.vm_1, self.vm_2]) + + try: + self.debug("SSHing into IP address: %s with VMs (ID: %s , %s) added to LB rule" % + ( + self.non_src_nat_ip.ipaddress, + self.vm_1.id, + self.vm_2.id + )) + #Create SSH client for each VM + ssh_1 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress, + self.services["lbrule"]["publicport"], + self.vm_1.username, + self.vm_1.password + ) + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.non_src_nat_ip.ipaddress) + + try: + self.debug("SSHing again into IP address: %s with VMs (ID: %s , %s) added to LB rule" % + ( + self.non_src_nat_ip.ipaddress, + self.vm_1.id, + self.vm_2.id + )) + ssh_2 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress, + self.services["lbrule"]["publicport"], + self.vm_2.username, + self.vm_2.password + ) + + # If Round Robin Algorithm is chosen, + # each ssh command should alternate between VMs + res_1 = ssh_1.execute("hostname")[0] + self.debug(res_1) + + time.sleep(self.services["lb_switch_wait"]) + + res_2 = ssh_2.execute("hostname")[0] + self.debug(res_2) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.non_src_nat_ip.ipaddress) + + self.assertIn( + self.vm_1.name, + res_1, + "Check if ssh succeeded for server1" + ) + self.assertIn( + self.vm_2.name, + res_2, + "Check if ssh succeeded for server2" + ) + + #Removing VM and assigning another VM to LB rule + lb_rule.remove(self.apiclient, [self.vm_2]) + + try: + self.debug("SSHing again into IP address: %s with VM (ID: %s) added to LB rule" % + ( + self.non_src_nat_ip.ipaddress, + self.vm_1.id, + )) + # Again make a SSH connection, as previous is not used after LB remove + ssh_1 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress, + self.services["lbrule"]["publicport"], + self.vm_1.username, + self.vm_1.password + ) + res_1 = ssh_1.execute("hostname")[0] + self.debug(res_1) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.non_src_nat_ip.ipaddress) + + self.assertIn( + self.vm_1.name, + res_1, + "Check if ssh succeeded for server1" + ) + + lb_rule.assign(self.apiclient, [self.vm_3]) + + try: + ssh_1 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress, + self.services["lbrule"]["publicport"], + self.vm_1.username, + self.vm_1.password + ) + ssh_3 = remoteSSHClient.remoteSSHClient( + self.non_src_nat_ip.ipaddress, + self.services["lbrule"]["publicport"], + self.vm_3.username, + self.vm_3.password + ) + + res_1 = ssh_1.execute("hostname")[0] + self.debug(res_1) + + time.sleep(self.services["lb_switch_wait"]) + + res_3 = ssh_3.execute("hostname")[0] + self.debug(res_3) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.non_src_nat_ip.ipaddress) + + self.assertIn( + self.vm_1.name, + res_1, + "Check if ssh succeeded for server1" + ) + self.assertIn( + self.vm_3.name, + res_3, + "Check if ssh succeeded for server3" + ) + return + + def tearDown(self): + cleanup_resources(self.apiclient, self.cleanup) + return + + +class TestReleaseIP(cloudstackTestCase): + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.services = Services().services + + # Get Zone, Domain and templates + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + template = get_template( + self.apiclient, + self.zone.id, + self.services["ostypeid"] + ) + self.services["server"]["zoneid"] = self.zone.id + + #Create an account, network, VM, Port forwarding rule, LB rules + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + + self.ip_address = PublicIPAddress.create( + self.apiclient, + self.account.account.name, + self.zone.id, + self.account.account.domainid + ) + + ip_addrs = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + try: + self.ip_addr = ip_addrs[0] + except Exception as e: + raise Exception("Failed: During acquiring source NAT for account: %s" % + self.account.account.name) + + self.nat_rule = NATRule.create( + self.apiclient, + self.virtual_machine, + self.services["natrule"], + self.ip_addr.id + ) + self.lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + self.ip_addr.id, + accountid=self.account.account.name + ) + self.cleanup = [ + self.virtual_machine, + self.account + ] + return + + def tearDown(self): + cleanup_resources(self.apiclient, self.cleanup) + + def test_releaseIP(self): + """Test for Associate/Disassociate public IP address""" + + self.debug("Deleting Public IP : %s" % self.ip_addr.id) + + self.ip_address.delete(self.apiclient) + + # Sleep to ensure that deleted state is reflected in other calls + time.sleep(self.services["sleep"]) + + # ListPublicIpAddresses should not list deleted Public IP address + list_pub_ip_addr_resp = list_publicIP( + self.apiclient, + id=self.ip_addr.id + ) + self.debug("List Public IP response" + str(list_pub_ip_addr_resp)) + + self.assertEqual( + list_pub_ip_addr_resp, + None, + "Check if disassociated IP Address is no longer available" + ) + + # ListPortForwardingRules should not list + # associated rules with Public IP address + list_nat_rule = list_nat_rules( + self.apiclient, + id=self.nat_rule.id + ) + self.debug("List NAT Rule response" + str(list_nat_rule)) + self.assertEqual( + list_nat_rule, + None, + "Check if PF rules are no longer available for IP address" + ) + + # listLoadBalancerRules should not list + # associated rules with Public IP address + list_lb_rule = list_lb_rules( + self.apiclient, + id=self.lb_rule.id + ) + self.debug("List LB Rule response" + str(list_lb_rule)) + + self.assertEqual( + list_lb_rule, + None, + "Check if LB rules for IP Address are no longer available" + ) + + # SSH Attempt though public IP should fail + with self.assertRaises(Exception): + ssh_2 = remoteSSHClient.remoteSSHClient( + self.ip_addr.ipaddress, + self.services["natrule"]["publicport"], + self.virtual_machine.username, + self.virtual_machine.password + ) + return + + +class TestDeleteAccount(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.services = Services().services + + # Get Zone, Domain and templates + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + template = get_template( + self.apiclient, + self.zone.id, + self.services["ostypeid"] + ) + self.services["server"]["zoneid"] = self.zone.id + + #Create an account, network, VM and IP addresses + self.account = Account.create( + self.apiclient, + self.services["account"], + admin=True, + domainid=self.domain.id + ) + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offering"] + ) + self.vm_1 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + + src_nat_ip_addrs = list_publicIP( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + try: + src_nat_ip_addr = src_nat_ip_addrs[0] + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + src_nat_ip_addr.ipaddress) + + self.lb_rule = LoadBalancerRule.create( + self.apiclient, + self.services["lbrule"], + src_nat_ip_addr.id, + self.account.account.name + ) + self.lb_rule.assign(self.apiclient, [self.vm_1]) + + self.nat_rule = NATRule.create( + self.apiclient, + self.vm_1, + self.services["natrule"], + src_nat_ip_addr.id + ) + self.cleanup = [] + return + + def test_delete_account(self): + """Test for delete account""" + + #Validate the Following + # 1. after account.cleanup.interval (global setting) + # time all the PF/LB rules should be deleted + # 2. verify that list(LoadBalancer/PortForwarding)Rules + # API does not return any rules for the account + # 3. The domR should have been expunged for this account + + self.account.delete(self.apiclient) + interval = list_configurations( + self.apiclient, + name='account.cleanup.interval' + ) + self.assertEqual( + isinstance(interval, list), + True, + "Check if account.cleanup.interval config present" + ) + # Sleep to ensure that all resources are deleted + time.sleep(int(interval[0].value)) + + # ListLoadBalancerRules should not list + # associated rules with deleted account + # Unable to find account testuser1 in domain 1 : Exception + try: + list_lb_reponse = list_lb_rules( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + list_lb_reponse, + None, + "Check load balancing rule is properly deleted." + ) + except Exception as e: + + raise Exception( + "Exception raised while fetching LB rules for account: %s" % + self.account.account.name) + # ListPortForwardingRules should not + # list associated rules with deleted account + try: + list_nat_reponse= list_nat_rules( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + list_nat_reponse, + None, + "Check load balancing rule is properly deleted." + ) + except Exception as e: + + raise Exception( + "Exception raised while fetching NAT rules for account: %s" % + self.account.account.name) + #Retrieve router for the user account + try: + routers = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + routers, + None, + "Check routers are properly deleted." + ) + except Exception as e: + + raise Exception( + "Exception raised while fetching routers for account: %s" % + self.account.account.name) + return + + def tearDown(self): + cleanup_resources(self.apiclient, self.cleanup) + return diff --git a/test/integration/smoke/test_primary_storage.py b/test/integration/smoke/test_primary_storage.py new file mode 100644 index 00000000000..efceebe3cda --- /dev/null +++ b/test/integration/smoke/test_primary_storage.py @@ -0,0 +1,238 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Primary Storage +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + +#Import System modules +import time + +class Services: + """Test Primary storage Services + """ + + def __init__(self): + self.services = { + "nfs": { + 0: { + "url": "nfs://192.168.100.131/testprimary", + # Format: File_System_Type/Location/Path + "name": "Primary XEN", + "hypervisor": 'XEN', + }, + 1: { + "url": "nfs://192.168.100.131/Primary", + "name": "Primary KVM", + "hypervisor": 'KVM', + }, + 2: { + "url": "nfs://192.168.100.131/Primary", + "name": "Primary VMWare", + "hypervisor": 'VMWare', + }, + }, + "iscsi": { + 0: { + "url": "iscsi://192.168.100.21/iqn.2012-01.localdomain.clo-cstack-cos6:iser/1", + # Format : iscsi://IP Address/IQN number/LUN# + "name": "Primary iSCSI", + "hypervisor": 'XEN', + }, + }, + } + +class TestPrimaryStorageServices(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.services = Services().services + self.cleanup = [] + # Get Zone and pod + self.zone = get_zone(self.apiclient, self.services) + self.pod = get_pod(self.apiclient, self.zone.id) + + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_primary_storage(self): + """Test primary storage pools - XEN, KVM, VMWare + """ + + # Validate the following: + # 1. verify hypervisortype returned by api is Xen/KVM/VMWare + # 2. verify that the cluster is in 'Enabled' allocation state + # 3. verify that the host is added successfully and + # in Up state with listHosts api response + + #Create NFS storage pools with on XEN/KVM/VMWare clusters + for k, v in self.services["nfs"].items(): + + clusters = list_clusters( + self.apiclient, + zoneid=self.zone.id, + hypervisortype=v["hypervisor"] + ) + self.assertEqual( + isinstance(clusters, list), + True, + "Check list response returns a valid list" + ) + cluster = clusters[0] + #Host should be present before adding primary storage + list_hosts_response = list_hosts( + self.apiclient, + clusterid=cluster.id + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check list Hosts for hypervisor: " + v["hypervisor"] + ) + + storage = StoragePool.create(self.apiclient, + v, + clusterid=cluster.id, + zoneid=self.zone.id, + podid=self.pod.id + ) + self.cleanup.append(storage) + + self.debug("Created storage pool in cluster: %s" % cluster.id) + + self.assertEqual( + storage.state, + 'Up', + "Check primary storage state for hypervisor: " + v["hypervisor"] + ) + + self.assertEqual( + storage.type, + 'NetworkFilesystem', + "Check storage pool type for hypervisor : " + v["hypervisor"] + ) + + #Verify List Storage pool Response has newly added storage pool + storage_pools_response = list_storage_pools( + self.apiclient, + id=storage.id, + ) + self.assertEqual( + isinstance(storage_pools_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(storage_pools_response), + 0, + "Check list Hosts response" + ) + + storage_response = storage_pools_response[0] + self.assertEqual( + storage_response.id, + storage.id, + "Check storage pool ID for hypervisor: " + v["hypervisor"] + ) + self.assertEqual( + storage.type, + storage_response.type, + "Check storage pool type for hypervisor: " + v["hypervisor"] + ) + # Call cleanup for reusing primary storage + cleanup_resources(self.apiclient, self.cleanup) + self.cleanup = [] + + # Create iSCSI storage pools with on XEN/KVM clusters + for k, v in self.services["iscsi"].items(): + clusters = list_clusters( + self.apiclient, + zoneid=self.zone.id, + hypervisortype=v["hypervisor"] + ) + self.assertEqual( + isinstance(clusters, list), + True, + "Check list response returns a valid list" + ) + cluster = clusters[0] + + storage = StoragePool.create(self.apiclient, + v, + clusterid=cluster.id, + zoneid=self.zone.id, + podid=self.pod.id + ) + self.cleanup.append(storage) + + self.debug("Created iSCSI storage pool in cluster: %s" % cluster.id) + + self.assertEqual( + storage.state, + 'Up', + "Check primary storage state for hypervisor: " + v["hypervisor"] + ) + + #Verify List Storage pool Response has newly added storage pool + storage_pools_response = list_storage_pools( + self.apiclient, + id=storage.id, + ) + self.assertEqual( + isinstance(storage_pools_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(storage_pools_response), + 0, + "Check Hosts response for hypervisor: " + v["hypervisor"] + ) + + storage_response = storage_pools_response[0] + self.assertEqual( + storage_response.id, + storage.id, + "Check storage pool ID for hypervisor: " + v["hypervisor"] + ) + self.assertEqual( + storage.type, + storage_response.type, + "Check storage pool type hypervisor: " + v["hypervisor"] + ) + + # Call cleanup for reusing primary storage + cleanup_resources(self.apiclient, self.cleanup) + self.cleanup = [] + return \ No newline at end of file diff --git a/test/integration/smoke/test_routers.py b/test/integration/smoke/test_routers.py new file mode 100644 index 00000000000..5937ddf00a3 --- /dev/null +++ b/test/integration/smoke/test_routers.py @@ -0,0 +1,820 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for routers +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + +#Import System modules +import time + + +class Services: + """Test router Services + """ + + def __init__(self): + self.services = { + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "virtual_machine": + { + "displayname": "Test VM", + "username": "root", + "password": "fr3sca", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "testuser", + "password": "fr3sca", + }, + "ostypeid":'5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "sleep": 60, + "timeout": 10, + "mode": 'advanced', #Networking mode: Basic, Advanced + } + + +class TestRouterServices(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = super( + TestRouterServices, + cls + ).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + + #Create an account, network, VM and IP addresses + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.vm_1 = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id + ) + cls.cleanup = [ + cls.vm_1, + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super( + TestRouterServices, + cls + ).getClsTestClient().getApiClient() + #Clean up, terminate the created templates + cleanup_resources(cls.api_client, cls.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + return + + def test_01_router_internal_basic(self): + """Test router internal basic zone + """ + # Validate the following + # 1. Router only does dhcp + # 2. Verify that ports 67 (DHCP) and 53 (DNS) are open on UDP + # by checking status of dnsmasq process + + # Find router associated with user account + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + hosts = list_hosts( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + virtualmachineid=self.vm_1.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list host returns a valid list" + ) + host = hosts[0] + + self.debug("Router ID: %s, state: %s" % (router.id, router.state)) + + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + + result = get_process_status( + host.ipaddress, + self.services['virtual_machine']["publicport"], + self.vm_1.username, + self.vm_1.password, + router.linklocalip, + "service dnsmasq status" + ) + res = str(result) + self.debug("Dnsmasq process status: %s" % res) + + self.assertEqual( + res.count("running"), + 1, + "Check dnsmasq service is running or not" + ) + return + + def test_02_router_internal_adv(self): + """Test router internal advanced zone + """ + # Validate the following + # 1. Router does dhcp, dns, gateway, LB, PF, FW + # 2. verify that dhcp, dns ports are open on UDP + # 3. dnsmasq, haproxy processes should be running + + # Find router associated with user account + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + + router = list_router_response[0] + + hosts = list_hosts( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + virtualmachineid=self.vm_1.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + self.debug("Router ID: %s, state: %s" % (router.id, router.state)) + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + + result = get_process_status( + host.ipaddress, + self.services['virtual_machine']["publicport"], + self.vm_1.username, + self.vm_1.password, + router.linklocalip, + "service dnsmasq status" + ) + res = str(result) + self.debug("Dnsmasq process status: %s" % res) + + self.assertEqual( + res.count("running"), + 1, + "Check dnsmasq service is running or not" + ) + + result = get_process_status( + host.ipaddress, + self.services['virtual_machine']["publicport"], + self.vm_1.username, + self.vm_1.password, + router.linklocalip, + "service haproxy status" + ) + res = str(result) + self.assertEqual( + res.count("running"), + 1, + "Check haproxy service is running or not" + ) + self.debug("Haproxy process status: %s" % res) + return + + def test_03_restart_network_cleanup(self): + """Test restart network + """ + + # Validate the following + # 1. When cleanup = true, router is destroyed and a new one created + # 2. New router will have new publicIp and linkLocalIp and + # all it's services should resume + + # Find router associated with user account + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + #Store old values before restart + old_linklocalip = router.linklocalip + + timeout = 10 + # Network should be in Implemented or Setup stage before restart + while True: + networks = list_networks( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(networks, list), + True, + "Check list response returns a valid list" + ) + network = networks[0] + if network.state in ["Implemented", "Setup"]: + break + elif timeout == 0: + break + else: + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + + self.debug( + "Restarting network with ID: %s, Network state: %s" % ( + network.id, + network.state + )) + cmd = restartNetwork.restartNetworkCmd() + cmd.id = network.id + cmd.cleanup = True + self.apiclient.restartNetwork(cmd) + + # Get router details after restart + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + self.assertNotEqual( + router.linklocalip, + old_linklocalip, + "Check link-local IP after restart" + ) + return + + def test_04_restart_network_wo_cleanup(self): + """Test restart network without cleanup + """ + + # Validate the following + # 1. When cleanup = false, router is restarted and + # all services inside the router are restarted + # 2. check 'uptime' to see if the actual restart happened + + timeout = 10 + # Network should be in Implemented or Setup stage before restart + while True: + networks = list_networks( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(networks, list), + True, + "Check list response returns a valid list" + ) + network = networks[0] + if network.state in ["Implemented", "Setup"]: + break + elif timeout == 0: + break + else: + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + + self.debug( + "Restarting network with ID: %s, Network state: %s" % ( + network.id, + network.state + )) + cmd = restartNetwork.restartNetworkCmd() + cmd.id = network.id + cmd.cleanup = False + self.apiclient.restartNetwork(cmd) + + # Get router details after restart + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + hosts = list_hosts( + self.apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + virtualmachineid=self.vm_1.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + res = get_process_status( + host.ipaddress, + self.services['virtual_machine']["publicport"], + self.vm_1.username, + self.vm_1.password, + router.linklocalip, + "uptime" + ) + + # res = 12:37:14 up 1 min, 0 users, load average: 0.61, 0.22, 0.08 + # Split result to check the uptime + result = res[0].split() + self.debug("Router Uptime: %s" % result) + self.assertEqual( + str(result[1]), + 'up', + "Check router is running or not" + ) + if str(result[3]) == "min,": + self.assertEqual( + (int(result[2]) < 3), + True, + "Check uptime is less than 3 mins or not" + ) + else: + self.assertEqual( + str(result[3]), + 'sec,', + "Check uptime is in seconds" + ) + return + + def test_05_router_basic(self): + """Test router basic setup + """ + + # Validate the following: + # 1. verify that listRouters returned a 'Running' router + # 2. router will have dns same as that seen in listZones + # 3. router will have a guestIP and a linkLocalIp" + + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_router_response), + 0, + "Check list router response" + ) + for router in list_router_response: + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + + zones = list_zones( + self.apiclient, + id=router.zoneid + ) + self.assertEqual( + isinstance(zones, list), + True, + "Check list response returns a valid list" + ) + zone = zones[0] + + self.assertEqual( + router.dns1, + zone.dns1, + "Compare DNS1 of router and zone" + ) + self.assertEqual( + router.dns2, + zone.dns2, + "Compare DNS2 of router and zone" + ) + self.assertEqual( + hasattr(router, 'guestipaddress'), + True, + "Check whether router has guest IP field" + ) + + self.assertEqual( + hasattr(router, 'linklocalip'), + True, + "Check whether router has link local IP field" + ) + return + + def test_06_router_advanced(self): + """Test router advanced setup + """ + + # Validate the following + # 1. verify that listRouters returned a 'Running' router + # 2. router will have dns and gateway as in listZones, listVlanIpRanges + # 3. router will have guest,public and linklocal IPs + + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_router_response), + 0, + "Check list router response" + ) + for router in list_router_response: + self.assertEqual( + router.state, + 'Running', + "Check list router response for router state" + ) + + zones = list_zones( + self.apiclient, + id=router.zoneid + ) + self.assertEqual( + isinstance(zones, list), + True, + "Check list response returns a valid list" + ) + zone = zones[0] + + self.assertEqual( + router.dns1, + zone.dns1, + "Compare DNS1 of router and zone" + ) + self.assertEqual( + router.dns2, + zone.dns2, + "Compare DNS2 of router and zone" + ) + self.assertEqual( + hasattr(router, 'guestipaddress'), + True, + "Check whether router has guest IP field" + ) + + self.assertEqual( + hasattr(router, 'linklocalip'), + True, + "Check whether router has link local IP field" + ) + + #Fetch corresponding ip ranges information from listVlanIpRanges + ipranges_response = list_vlan_ipranges( + self.apiclient, + zoneid=router.zoneid + ) + self.assertEqual( + isinstance(ipranges_response, list), + True, + "Check list response returns a valid list" + ) + iprange = ipranges_response[0] + self.assertEqual( + router.gateway, + iprange.gateway, + "Check gateway with that of corresponding IP range" + ) + return + + def test_07_stop_router(self): + """Test stop router + """ + + # Validate the following + # 1. listRouter should report the router for the account as stopped + + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + self.debug("Stopping the router with ID: %s" % router.id) + #Stop the router + cmd = stopRouter.stopRouterCmd() + cmd.id = router.id + self.apiclient.stopRouter(cmd) + + #List routers to check state of router + router_response = list_routers( + self.apiclient, + id=router.id + ) + self.assertEqual( + isinstance(router_response, list), + True, + "Check list response returns a valid list" + ) + #List router should have router in stopped state + self.assertEqual( + router_response[0].state, + 'Stopped', + "Check list router response for router state" + ) + return + + def test_08_start_router(self): + """Test start router + """ + + # Validate the following + # 1. listRouter should report the router for the account as stopped + + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + self.debug("Starting the router with ID: %s" % router.id) + + #Start the router + cmd = startRouter.startRouterCmd() + cmd.id = router.id + self.apiclient.startRouter(cmd) + + #List routers to check state of router + router_response = list_routers( + self.apiclient, + id=router.id + ) + self.assertEqual( + isinstance(router_response, list), + True, + "Check list response returns a valid list" + ) + #List router should have router in running state + self.assertEqual( + router_response[0].state, + 'Running', + "Check list router response for router state" + ) + return + + def test_09_reboot_router(self): + """Test reboot router + """ + + # Validate the following + # 1. listRouter should report the router for the account as stopped + + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + public_ip = router.publicip + + self.debug("Rebooting the router with ID: %s" % router.id) + + #Reboot the router + cmd = rebootRouter.rebootRouterCmd() + cmd.id = router.id + self.apiclient.rebootRouter(cmd) + + #List routers to check state of router + router_response = list_routers( + self.apiclient, + id=router.id + ) + self.assertEqual( + isinstance(router_response, list), + True, + "Check list response returns a valid list" + ) + #List router should have router in running state and same public IP + self.assertEqual( + router_response[0].state, + 'Running', + "Check list router response for router state" + ) + + self.assertEqual( + router_response[0].publicip, + public_ip, + "Check list router response for router public IP" + ) + return + + def test_10_network_gc(self): + """Test network GC + """ + + # Validate the following + # 1. stop All User VMs in the account + # 2. wait for network.gc.interval time" + # 3. After network.gc.interval, router should be stopped + # 4. ListRouters should return the router in Stopped state + + list_vms = list_virtual_machines( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_vms, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_vms), + 0, + "Check length of list VM response" + ) + + for vm in list_vms: + self.debug("Stopping the VM with ID: %s" % vm.id) + # Stop all virtual machines associated with that account + cmd = stopVirtualMachine.stopVirtualMachineCmd() + cmd.id = vm.id + self.apiclient.stopVirtualMachine(cmd) + + # Get network.gc.interval config value + config = list_configurations( + self.apiclient, + name='network.gc.interval' + ) + self.assertEqual( + isinstance(config, list), + True, + "Check list response returns a valid list" + ) + gcinterval = config[0] + + # Get network.gc.wait config value + config = list_configurations( + self.apiclient, + name='network.gc.wait' + ) + self.assertEqual( + isinstance(config, list), + True, + "Check list response returns a valid list" + ) + gcwait = config[0] + + total_wait = int(gcinterval.value) + int(gcwait.value) + # Wait for wait_time * 2 time to cleanup all the resources + time.sleep(total_wait * 2) + + timeout = self.services["timeout"] + while True: + #Check status of network router + list_router_response = list_routers( + self.apiclient, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + if isinstance(list_router_response, list): + break + elif timeout == 0: + raise Exception("List router call failed!") + time.sleep(5) + timeout = timeout -1 + + self.assertEqual( + isinstance(list_router_response, list), + True, + "Check list response returns a valid list" + ) + router = list_router_response[0] + + self.debug("Router state after network.gc.interval: %s" % router.state) + self.assertEqual( + router.state, + 'Stopped', + "Check state of the router after stopping all VMs associated" + ) + return diff --git a/test/integration/smoke/test_secondary_storage.py b/test/integration/smoke/test_secondary_storage.py new file mode 100644 index 00000000000..9f60593aa9b --- /dev/null +++ b/test/integration/smoke/test_secondary_storage.py @@ -0,0 +1,380 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Secondary Storage +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + +#Import System modules +import time + +class Services: + """Test secondary storage Services + """ + + def __init__(self): + self.services = { + "storage": { + "url": "nfs://192.168.100.131/SecStorage" + # Format: File_System_Type/Location/Path + }, + "hypervisors": { + 0: { + "hypervisor": "XenServer", + "templatefilter": "self", + }, + 1: { + "hypervisor": "KVM", + "templatefilter": "self", + }, + 2: { + "hypervisor": "VMWare", + "templatefilter": "self", + }, + }, + "sleep": 60, + "timeout": 5, + } + +class TestSecStorageServices(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestSecStorageServices, cls).getClsTestClient().getApiClient() + cls.services = Services().services + cls._cleanup = [] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.cleanup = [] + self.services = Services().services + # Get Zone and pod + self.domain = get_domain(self.apiclient, self.services) + self.zone = get_zone(self.apiclient, self.services) + self.pod = get_pod(self.apiclient, self.zone.id) + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_add_sec_storage(self): + """Test secondary storage + """ + + # Validate the following: + # 1. secondary storage should be added to the zone. + # 2. Verify with listHosts and type secondarystorage + + cmd = addSecondaryStorage.addSecondaryStorageCmd() + cmd.zoneid = self.zone.id + cmd.url = self.services["storage"]["url"] + sec_storage = self.apiclient.addSecondaryStorage(cmd) + + self.debug("Added secondary storage to zone: %s" % self.zone.id) + # Cleanup at the end + self._cleanup.append(sec_storage) + + self.assertEqual( + sec_storage.zoneid, + self.zone.id, + "Check zoneid where sec storage is added" + ) + + list_hosts_response = list_hosts( + self.apiclient, + type='SecondaryStorage', + id=sec_storage.id + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check list Hosts response" + ) + + host_response = list_hosts_response[0] + #Check if host is Up and running + self.assertEqual( + host_response.id, + sec_storage.id, + "Check ID of secondary storage" + ) + self.assertEqual( + sec_storage.type, + host_response.type, + "Check type of host from list hosts response" + ) + return + + def test_02_sys_vm_start(self): + """Test system VM start + """ + + # 1. verify listHosts has all 'routing' hosts in UP state + # 2. verify listStoragePools shows all primary storage pools + # in UP state + # 3. verify that secondary storage was added successfully + + list_hosts_response = list_hosts( + self.apiclient, + type='Routing', + zoneid=self.zone.id, + podid=self.pod.id + ) + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + # ListHosts has all 'routing' hosts in UP state + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check list host response" + ) + for host in list_hosts_response: + self.assertEqual( + host.state, + 'Up', + "Check state of routing hosts is Up or not" + ) + + # ListStoragePools shows all primary storage pools in UP state + list_storage_response = list_storage_pools( + self.apiclient, + zoneid=self.zone.id, + podid=self.pod.id + ) + self.assertEqual( + isinstance(list_storage_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_storage_response), + 0, + "Check list storage pools response" + ) + + for primary_storage in list_hosts_response: + self.assertEqual( + primary_storage.state, + 'Up', + "Check state of primary storage pools is Up or not" + ) + + # Secondary storage is added successfully + timeout = self.services["timeout"] + while True: + list_hosts_response = list_hosts( + self.apiclient, + type='SecondaryStorage', + zoneid=self.zone.id, + ) + + if not isinstance(list_hosts_response, list): + # Sleep to ensure Secondary storage is Up + time.sleep(int(self.services["sleep"])) + timeout = timeout - 1 + elif timeout == 0 or isinstance(list_hosts_response, list): + break + + self.assertEqual( + isinstance(list_hosts_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_hosts_response), + 0, + "Check list Hosts response" + ) + + host_response = list_hosts_response[0] + #Check if host is Up and running + self.assertEqual( + host_response.state, + 'Up', + "Check state of secondary storage" + ) + self.debug("Checking SSVM status in zone: %s" % self.zone.id) + + timeout = self.services["timeout"] + + while True: + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + zoneid=self.zone.id, + podid=self.pod.id + ) + if not isinstance(list_ssvm_response, list): + # Sleep to ensure SSVMs are Up and Running + time.sleep(int(self.services["sleep"])) + timeout = timeout - 1 + elif timeout == 0 or isinstance(list_ssvm_response, list): + break + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + #Verify SSVM response + self.assertNotEqual( + len(list_ssvm_response), + 0, + "Check list System VMs response" + ) + + for ssvm in list_ssvm_response: + self.assertEqual( + ssvm.state, + 'Running', + "Check whether state of SSVM is running" + ) + return + + def test_03_sys_template_ready(self): + """Test system templates are ready + """ + + # Validate the following + # If SSVM is in UP state and running + # 1. wait for listTemplates to show all builtin templates + # downloaded for all added hypervisors and in “Ready” state" + + for k, v in self.services["hypervisors"].items(): + + self.debug("Downloading BUILTIN templates in zone: %s" % + self.zone.id) + + list_template_response = list_templates( + self.apiclient, + hypervisor=v["hypervisor"], + zoneid=self.zone.id, + templatefilter=v["templatefilter"], + listall=True, + account='system', + domainid=self.domain.id + ) + + # Ensure all BUILTIN templates are downloaded + templateid = None + for template in list_template_response: + if template.templatetype == "BUILTIN": + templateid = template.id + + # Wait to start a downloading of template + time.sleep(self.services["sleep"]) + + while True and (templateid != None): + + timeout = self.services["timeout"] + while True: + template_response = list_templates( + self.apiclient, + id=templateid, + zoneid=self.zone.id, + templatefilter=v["templatefilter"], + listall=True, + account='system', + domainid=self.domain.id + ) + + if isinstance(template_response, list): + template = template_response[0] + break + + elif timeout == 0: + raise Exception("List template API call failed.") + + time.sleep(1) + timeout = timeout - 1 + + # If template is ready, + # template.status = Download Complete + # Downloading - x% Downloaded + # Error - Any other string + if template.status == 'Download Complete' : + break + elif 'Downloaded' not in template.status.split(): + raise Exception + elif 'Downloaded' in template.status.split(): + time.sleep(self.services["sleep"]) + + #Ensuring the template is in ready state + time.sleep(self.services["sleep"]) + + timeout = self.services["timeout"] + while True: + template_response = list_templates( + self.apiclient, + id=templateid, + zoneid=self.zone.id, + templatefilter=v["templatefilter"], + listall=True, + account='system', + domainid=self.domain.id + ) + + if isinstance(template_response, list): + template = template_response[0] + break + + elif timeout == 0: + raise Exception("List template API call failed.") + + time.sleep(1) + timeout = timeout - 1 + + self.assertEqual( + isinstance(template_response, list), + True, + "Check list response returns a valid list" + ) + template = template_response[0] + + self.assertEqual( + template.isready, + True, + "Check whether state of template is ready or not" + ) + return \ No newline at end of file diff --git a/test/integration/smoke/test_service_offerings.py b/test/integration/smoke/test_service_offerings.py new file mode 100644 index 00000000000..bf9ccd14a6c --- /dev/null +++ b/test/integration/smoke/test_service_offerings.py @@ -0,0 +1,238 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Service offerings""" + +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * + + +class Services: + """Test Service offerings Services + """ + + def __init__(self): + self.services = { + "off": + { + "name": "Service Offering", + "displaytext": "Service Offering", + "cpunumber": 1, + "cpuspeed": 100, # MHz + "memory": 64, # in MBs + }, + } + +class TestCreateServiceOffering(cloudstackTestCase): + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + self.services = Services().services + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_create_service_offering(self): + """Test to create service offering""" + + # Validate the following: + # 1. createServiceOfferings should return a valid information for newly created offering + # 2. The Cloud Database contains the valid information + + service_offering = ServiceOffering.create( + self.apiclient, + self.services["off"] + ) + self.cleanup.append(service_offering) + + self.debug("Created service offering with ID: %s" % service_offering.id) + + list_service_response = list_service_offering( + self.apiclient, + id=service_offering.id + ) + self.assertEqual( + isinstance(list_service_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_service_response), + 0, + "Check Service offering is created" + ) + service_response = list_service_response[0] + + self.assertEqual( + list_service_response[0].cpunumber, + self.services["off"]["cpunumber"], + "Check server id in createServiceOffering" + ) + self.assertEqual( + list_service_response[0].cpuspeed, + self.services["off"]["cpuspeed"], + "Check cpuspeed in createServiceOffering" + ) + self.assertEqual( + list_service_response[0].displaytext, + self.services["off"]["displaytext"], + "Check server displaytext in createServiceOfferings" + ) + self.assertEqual( + list_service_response[0].memory, + self.services["off"]["memory"], + "Check memory in createServiceOffering" + ) + self.assertEqual( + list_service_response[0].name, + self.services["off"]["name"], + "Check name in createServiceOffering" + ) + return + + +class TestServiceOfferings(cloudstackTestCase): + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestServiceOfferings, cls).getClsTestClient().getApiClient() + cls.service_offering_1 = ServiceOffering.create( + cls.api_client, + cls.services["off"] + ) + cls.service_offering_2 = ServiceOffering.create( + cls.api_client, + cls.services["off"] + ) + cls._cleanup = [cls.service_offering_1] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestServiceOfferings, cls).getClsTestClient().getApiClient() + #Clean up, terminate the created templates + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_02_edit_service_offering(self): + """Test to update existing service offering""" + + # Validate the following: + # 1. updateServiceOffering should return + # a valid information for newly created offering + + #Generate new name & displaytext from random data + random_displaytext = random_gen() + random_name = random_gen() + + self.debug("Updating service offering with ID: %s" % + self.service_offering_1.id) + + cmd = updateServiceOffering.updateServiceOfferingCmd() + #Add parameters for API call + cmd.id = self.service_offering_1.id + cmd.displaytext = random_displaytext + cmd.name = random_name + self.apiclient.updateServiceOffering(cmd) + + list_service_response = list_service_offering( + self.apiclient, + id=self.service_offering_1.id + ) + self.assertEqual( + isinstance(list_service_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_service_response), + 0, + "Check Service offering is updated" + ) + + self.assertEqual( + list_service_response[0].displaytext, + random_displaytext, + "Check server displaytext in updateServiceOffering" + ) + self.assertEqual( + list_service_response[0].name, + random_name, + "Check server name in updateServiceOffering" + ) + + return + + def test_03_delete_service_offering(self): + """Test to delete service offering""" + + # Validate the following: + # 1. deleteServiceOffering should return + # a valid information for newly created offering + + self.debug("Deleting service offering with ID: %s" % + self.service_offering_2.id) + + self.service_offering_2.delete(self.apiclient) + + list_service_response = list_service_offering( + self.apiclient, + id=self.service_offering_2.id + ) + + self.assertEqual( + list_service_response, + None, + "Check if service offering exists in listDiskOfferings" + ) + + return diff --git a/test/integration/smoke/test_snapshots.py b/test/integration/smoke/test_snapshots.py new file mode 100644 index 00000000000..6b3ec704ff0 --- /dev/null +++ b/test/integration/smoke/test_snapshots.py @@ -0,0 +1,1159 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Snapshots +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient + + +class Services: + """Test Snapshots Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "server_with_disk": + { + "displayname": "Test VM -With Disk", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + + "server_without_disk": + { + "displayname": "Test VM-No Disk", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + # For NAT rule creation + "publicport": 22, + "protocol": 'TCP', + }, + + "recurring_snapshot": + { + "intervaltype": 'HOURLY', + # Frequency of snapshots + "maxsnaps": 1, # Should be min 2 + "schedule": 1, + "timezone": 'US/Arizona', + # Timezone Formats - http://cloud.mindtouch.us/CloudStack_Documentation/Developer's_Guide%3A_CloudStack + }, + + "templates": + { + "displaytext": 'Template from snapshot', + "name": 'Template from snapshot', + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "templatefilter": 'self', + }, + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + # Cent OS 5.3 (64 bit) + "diskdevice": "/dev/xvdb", # Data Disk + "rootdisk": "/dev/xvda", # Root Disk + + "diskname": "Test Disk", + "size": 1, # GBs + + "mount_dir": "/mnt/tmp", + "sub_dir": "test", + "sub_lvl_dir1": "test1", + "sub_lvl_dir2": "test2", + "random_data": "random.data", + + "username": "root", + "password": "password", + "ssh_port": 22, + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode, Advanced, Basic + } + + +class TestSnapshotRootDisk(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestSnapshotRootDisk, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["server_without_disk"]["zoneid"] = cls.zone.id + cls.services["template"] = template.id + cls.services["zoneid"] = cls.zone.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = cls.virtual_machine_with_disk = \ + VirtualMachine.create( + cls.api_client, + cls.services["server_without_disk"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + cls._cleanup = [ + cls.service_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_snapshot_root_disk(self): + """Test Snapshot Root Disk + """ + + # Validate the following + # 1. listSnapshots should list the snapshot that was created. + # 2. verify that secondary storage NFS share contains + # the reqd volume under + # /secondary/snapshots//$account_id/$volumeid/$snapshot_uuid + # 3. verify backup_snap_id was non null in the `snapshots` table + + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine_with_disk.id, + type='ROOT', + listall=True + ) + + snapshot = Snapshot.create( + self.apiclient, + volumes[0].id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Snapshot created: ID - %s" % snapshot.id) + + snapshots = list_snapshots( + self.apiclient, + id=snapshot.id + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list item call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check resource id in list resources call" + ) + self.debug( + "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % str(snapshot.id) + ) + qresultset = self.dbclient.execute( + "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % str(snapshot.id) + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = qresultset[0] + + snapshot_uuid = qresult[0] # backup_snap_id = snapshot UUID + account_id = qresult[1] + volume_id = qresult[2] + + self.assertNotEqual( + str(snapshot_uuid), + 'NULL', + "Check if backup_snap_id is not null" + ) + + # Get the Secondary Storage details from list Hosts + hosts = list_hosts( + self.apiclient, + type='SecondaryStorage', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + uuids = [] + for host in hosts: + # hosts[0].name = "nfs://192.168.100.21/export/test" + parse_url = (host.name).split('/') + # parse_url = ['nfs:', '', '192.168.100.21', 'export', 'test'] + + # Split IP address and export path from name + sec_storage_ip = parse_url[2] + # Sec Storage IP: 192.168.100.21 + + export_path = '/'.join(parse_url[3:]) + # Export path: export/test + + try: + # Login to VM to check snapshot present on sec disk + ssh_client = self.virtual_machine_with_disk.get_ssh_client() + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s/%s %s" % ( + sec_storage_ip, + export_path, + self.services["mount_dir"] + ), + "ls %s/snapshots/%s/%s" % ( + self.services["mount_dir"], + account_id, + volume_id + ), + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine_with_disk.ipaddress) + + uuids.append(result) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + try: + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception as e: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine_with_disk.ipaddress) + + res = str(uuids) + # Check snapshot UUID in secondary storage and database + self.assertEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + return + + +class TestSnapshots(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestSnapshots, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + + cls.services["domainid"] = cls.domain.id + cls.services["server_with_disk"]["zoneid"] = cls.zone.id + cls.services["server_with_disk"]["diskoffering"] = cls.disk_offering.id + + cls.services["server_without_disk"]["zoneid"] = cls.zone.id + + cls.services["template"] = template.id + cls.services["zoneid"] = cls.zone.id + cls.services["diskoffering"] = cls.disk_offering.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = cls.virtual_machine_with_disk = \ + VirtualMachine.create( + cls.api_client, + cls.services["server_with_disk"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + cls.virtual_machine_without_disk = \ + VirtualMachine.create( + cls.api_client, + cls.services["server_without_disk"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account, + ] + return + + @classmethod + def tearDownClass(cls): + try: + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + #Clean up, terminate the created instance, volumes and snapshots + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_02_snapshot_data_disk(self): + """Test Snapshot Data Disk + """ + + volume = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine_with_disk.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(volume, list), + True, + "Check list response returns a valid list" + ) + + self.debug("Creating a Snapshot from data volume: %s" % volume[0].id) + snapshot = Snapshot.create( + self.apiclient, + volume[0].id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + snapshots = list_snapshots( + self.apiclient, + id=snapshot.id + ) + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + snapshots, + None, + "Check if result exists in list item call" + ) + self.assertEqual( + snapshots[0].id, + snapshot.id, + "Check resource id in list resources call" + ) + self.debug( + "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % str(snapshot.id) + ) + qresultset = self.dbclient.execute( + "select backup_snap_id, account_id, volume_id from snapshots where uuid = '%s';" \ + % str(snapshot.id) + ) + self.assertNotEqual( + len(qresultset), + 0, + "Check DB Query result set" + ) + + qresult = qresultset[0] + snapshot_uuid = qresult[0] # backup_snap_id = snapshot UUID + account_id = qresult[1] + volume_id = qresult[2] + + self.assertNotEqual( + str(snapshot_uuid), + 'NULL', + "Check if backup_snap_id is not null" + ) + + # Get the Secondary Storage details from list Hosts + hosts = list_hosts( + self.apiclient, + type='SecondaryStorage', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + uuids = [] + for host in hosts: + # hosts[0].name = "nfs://192.168.100.21/export" + parse_url = (host.name).split('/') + # parse_url = ['nfs:', '', '192.168.100.21', 'export'] + + # Split IP address and export path from name + sec_storage_ip = parse_url[2] + # Sec Storage IP: 192.168.100.21 + + export_path = '/'.join(parse_url[3:]) + # Export path: export + + try: + # Login to VM to check snapshot present on sec disk + ssh_client = self.virtual_machine_with_disk.get_ssh_client() + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s/%s %s" % ( + sec_storage_ip, + export_path, + self.services["mount_dir"] + ), + "ls %s/snapshots/%s/%s" % ( + self.services["mount_dir"], + account_id, + volume_id + ), + ] + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.virtual_machine_with_disk.ipaddress) + + uuids.append(result) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + try: + for c in cmds: + self.debug(c) + ssh_client.execute(c) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.virtual_machine_with_disk.ipaddress) + + res = str(uuids) + # Check snapshot UUID in secondary storage and database + self.assertEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + return + + def test_03_volume_from_snapshot(self): + """Create volumes from snapshots + """ + #1. Login to machine; create temp/test directories on data volume + #2. Snapshot the Volume + #3. Create another Volume from snapshot + #4. Mount/Attach volume to another server + #5. Compare data + random_data_0 = random_gen(100) + random_data_1 = random_gen(100) + + try: + ssh_client = self.virtual_machine.get_ssh_client() + + #Format partition using ext3 + format_volume_to_ext3( + ssh_client, + self.services["diskdevice"] + ) + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s1 %s" % ( + self.services["diskdevice"], + self.services["mount_dir"] + ), + "mkdir -p %s/%s/{%s,%s} " % ( + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir1"], + self.services["sub_lvl_dir2"] + ), + "echo %s > %s/%s/%s/%s" % ( + random_data_0, + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir1"], + self.services["random_data"] + ), + "echo %s > %s/%s/%s/%s" % ( + random_data_1, + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir2"], + self.services["random_data"] + ), + ] + for c in cmds: + self.debug(c) + ssh_client.execute(c) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.virtual_machine.ipaddress) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + + try: + for c in cmds: + self.debug(c) + ssh_client.execute(c) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.virtual_machine.ipaddress) + + list_volume_response = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + + volume_response = list_volume_response[0] + #Create snapshot from attached volume + snapshot = Snapshot.create( + self.apiclient, + volume_response.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.debug("Created Snapshot from volume: %s" % volume_response.id) + + #Create volume from snapshot + self.debug("Creating volume from snapshot: %s" % snapshot.id) + volume = Volume.create_from_snapshot( + self.apiclient, + snapshot.id, + self.services, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + volumes = list_volumes( + self.apiclient, + id=volume.id + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(volumes), + None, + "Check Volume list Length" + ) + + self.assertEqual( + volumes[0].id, + volume.id, + "Check Volume in the List Volumes" + ) + #Attaching volume to new VM + new_virtual_machine = self.virtual_machine_without_disk + self.cleanup.append(new_virtual_machine) + + cmd = attachVolume.attachVolumeCmd() + cmd.id = volume.id + cmd.virtualmachineid = new_virtual_machine.id + self.apiclient.attachVolume(cmd) + + try: + #Login to VM to verify test directories and files + ssh = new_virtual_machine.get_ssh_client() + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s1 %s" % ( + self.services["diskdevice"], + self.services["mount_dir"] + ), + ] + + for c in cmds: + self.debug(c) + result = ssh.execute(c) + self.debug(result) + + returned_data_0 = ssh.execute("cat %s/%s/%s/%s" % ( + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir1"], + self.services["random_data"] + )) + returned_data_1 = ssh.execute("cat %s/%s/%s/%s" % ( + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir2"], + self.services["random_data"] + )) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.new_virtual_machine.ipaddress) + + #Verify returned data + self.assertEqual( + random_data_0, + returned_data_0[0], + "Verify newly attached volume contents with existing one" + ) + self.assertEqual( + random_data_1, + returned_data_1[0], + "Verify newly attached volume contents with existing one" + ) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + try: + for c in cmds: + ssh_client.execute(c) + + except Exception as e: + self.fail("SSH failed for VM with IP: %s" % + self.new_virtual_machine.ipaddress) + return + + def test_04_delete_snapshot(self): + """Test Delete Snapshot + """ + + #1. Snapshot the Volume + #2. Delete the snapshot + #3. Verify snapshot is removed by calling List Snapshots API + + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='DATADISK', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check list response returns a valid list" + ) + snapshot = Snapshot.create( + self.apiclient, + volumes[0].id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + snapshot.delete(self.apiclient) + + snapshots = list_snapshots( + self.apiclient, + id=snapshot.id + ) + + self.assertEqual( + snapshots, + None, + "Check if result exists in list item call" + ) + return + + def test_05_recurring_snapshot_root_disk(self): + """Test Recurring Snapshot Root Disk + """ + #1. Create snapshot policy for root disk + #2. ListSnapshot policy should return newly created policy + #3. Verify only most recent number (maxsnaps) snapshots retailed + + volume = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine_with_disk.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volume, list), + True, + "Check list response returns a valid list" + ) + recurring_snapshot = SnapshotPolicy.create( + self.apiclient, + volume[0].id, + self.services["recurring_snapshot"] + ) + self.cleanup.append(recurring_snapshot) + + #ListSnapshotPolicy should return newly created policy + list_snapshots_policy = list_snapshot_policy( + self.apiclient, + id=recurring_snapshot.id, + volumeid=volume[0].id + ) + self.assertEqual( + isinstance(list_snapshots_policy, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_snapshots_policy, + None, + "Check if result exists in list item call" + ) + snapshots_policy = list_snapshots_policy[0] + self.assertEqual( + snapshots_policy.id, + recurring_snapshot.id, + "Check recurring snapshot id in list resources call" + ) + self.assertEqual( + snapshots_policy.maxsnaps, + self.services["recurring_snapshot"]["maxsnaps"], + "Check interval type in list resources call" + ) + # Sleep for (maxsnaps+1) hours to verify + # only maxsnaps snapshots are retained + time.sleep( + (self.services["recurring_snapshot"]["maxsnaps"]) * 3600 + ) + + timeout = self.services["timeout"] + while True: + snapshots = list_snapshots( + self.apiclient, + volumeid=volume[0].id, + intervaltype=\ + self.services["recurring_snapshot"]["intervaltype"], + snapshottype='RECURRING', + listall=True + ) + + if isinstance(snapshots, list): + break + + elif timeout == 0: + raise Exception("List snapshots API call failed.") + + time.sleep(1) + timeout = timeout - 1 + + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + + self.assertEqual( + len(snapshots), + self.services["recurring_snapshot"]["maxsnaps"], + "Check maximum number of recurring snapshots retained" + ) + return + + def test_06_recurring_snapshot_data_disk(self): + """Test Recurring Snapshot data Disk + """ + #1. Create snapshot policy for data disk + #2. ListSnapshot policy should return newly created policy + #3. Verify only most recent number (maxsnaps) snapshots retailed + + volume = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine_with_disk.id, + type='DATADISK', + listall=True + ) + + self.assertEqual( + isinstance(volume, list), + True, + "Check list response returns a valid list" + ) + + recurring_snapshot = SnapshotPolicy.create( + self.apiclient, + volume[0].id, + self.services["recurring_snapshot"] + ) + self.cleanup.append(recurring_snapshot) + #ListSnapshotPolicy should return newly created policy + list_snapshots_policy = list_snapshot_policy( + self.apiclient, + id=recurring_snapshot.id, + volumeid=volume[0].id + ) + + self.assertEqual( + isinstance(list_snapshots_policy, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + list_snapshots_policy, + None, + "Check if result exists in list item call" + ) + snapshots_policy = list_snapshots_policy[0] + self.assertEqual( + snapshots_policy.id, + recurring_snapshot.id, + "Check recurring snapshot id in list resources call" + ) + self.assertEqual( + snapshots_policy.maxsnaps, + self.services["recurring_snapshot"]["maxsnaps"], + "Check interval type in list resources call" + ) + + # Sleep for (maxsnaps) hours to verify only maxsnaps snapshots are + # retained + time.sleep( + (self.services["recurring_snapshot"]["maxsnaps"]) * 3600 + ) + + timeout = self.services["timeout"] + while True: + snapshots = list_snapshots( + self.apiclient, + volumeid=volume[0].id, + intervaltype=\ + self.services["recurring_snapshot"]["intervaltype"], + snapshottype='RECURRING', + listall=True + ) + + if isinstance(snapshots, list): + break + + elif timeout == 0: + raise Exception("List snapshots API call failed.") + + time.sleep(1) + timeout = timeout - 1 + + self.assertEqual( + isinstance(snapshots, list), + True, + "Check list response returns a valid list" + ) + self.assertEqual( + len(snapshots), + self.services["recurring_snapshot"]["maxsnaps"], + "Check maximum number of recurring snapshots retained" + ) + return + + def test_07_template_from_snapshot(self): + """Create Template from snapshot + """ + + #1. Login to machine; create temp/test directories on data volume + #2. Snapshot the Volume + #3. Create Template from snapshot + #4. Deploy Virtual machine using this template + #5. Login to newly created virtual machine + #6. Compare data + + random_data_0 = random_gen(100) + random_data_1 = random_gen(100) + + try: + #Login to virtual machine + ssh_client = self.virtual_machine.get_ssh_client() + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s1 %s" % ( + self.services["rootdisk"], + self.services["mount_dir"] + ), + "mkdir -p %s/%s/{%s,%s} " % ( + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir1"], + self.services["sub_lvl_dir2"] + ), + "echo %s > %s/%s/%s/%s" % ( + random_data_0, + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir1"], + self.services["random_data"] + ), + "echo %s > %s/%s/%s/%s" % ( + random_data_1, + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir2"], + self.services["random_data"] + ), + "sync", + ] + + for c in cmds: + self.debug(c) + result = ssh_client.execute(c) + self.debug(result) + + except Exception as e: + self.fail("SSH failed for VM with IP address: %s" % + self.virtual_machine.ipaddress) + + # Unmount the Volume + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + for c in cmds: + self.debug(c) + ssh_client.execute(c) + + volumes = list_volumes( + self.apiclient, + virtualmachineid=self.virtual_machine.id, + type='ROOT', + listall=True + ) + self.assertEqual( + isinstance(volumes, list), + True, + "Check list response returns a valid list" + ) + + volume = volumes[0] + + #Create a snapshot of volume + snapshot = Snapshot.create( + self.apiclient, + volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.debug("Snapshot created from volume ID: %s" % volume.id) + # Generate template from the snapshot + template = Template.create_from_snapshot( + self.apiclient, + snapshot, + self.services["templates"] + ) + self.cleanup.append(template) + self.debug("Template created from snapshot ID: %s" % snapshot.id) + + # Verify created template + templates = list_templates( + self.apiclient, + templatefilter=\ + self.services["templates"]["templatefilter"], + id=template.id + ) + self.assertNotEqual( + templates, + None, + "Check if result exists in list item call" + ) + + self.assertEqual( + templates[0].id, + template.id, + "Check new template id in list resources call" + ) + self.debug("Deploying new VM from template: %s" % template.id) + + # Deploy new virtual machine using template + new_virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["server_without_disk"], + templateid=template.id, + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id, + mode=self.services["mode"] + ) + self.cleanup.append(new_virtual_machine) + + try: + #Login to VM & mount directory + ssh = new_virtual_machine.get_ssh_client() + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount %s1 %s" % ( + self.services["rootdisk"], + self.services["mount_dir"] + ) + ] + + for c in cmds: + ssh.execute(c) + + returned_data_0 = ssh.execute("cat %s/%s/%s/%s" % ( + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir1"], + self.services["random_data"] + )) + self.debug(returned_data_0) + returned_data_1 = ssh.execute("cat %s/%s/%s/%s" % ( + self.services["mount_dir"], + self.services["sub_dir"], + self.services["sub_lvl_dir2"], + self.services["random_data"] + )) + self.debug(returned_data_1) + except Exception as e: + self.fail("SSH failed for VM with IP address: %s" % + new_virtual_machine.ipaddress) + #Verify returned data + self.assertEqual( + random_data_0, + returned_data_0[0], + "Verify newly attached volume contents with existing one" + ) + self.assertEqual( + random_data_1, + returned_data_1[0], + "Verify newly attached volume contents with existing one" + ) + # Unmount the volume + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + try: + for c in cmds: + self.debug(c) + ssh_client.execute(c) + + except Exception as e: + self.fail("SSH failed for VM with IP address: %s" % + new_virtual_machine.ipaddress) + return diff --git a/test/integration/smoke/test_ssvm.py b/test/integration/smoke/test_ssvm.py new file mode 100644 index 00000000000..bcc4dba9848 --- /dev/null +++ b/test/integration/smoke/test_ssvm.py @@ -0,0 +1,916 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for SSVM +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +import telnetlib + +#Import System modules +import time + +class Services: + """Test SSVM Services + """ + + def __init__(self): + self.services = { + "host": { + "username": 'root', # Credentials for SSH + "password": 'fr3sca', + "publicport": 22, + }, + "sleep": 60, + "timeout": 10, + } + +class TestSSVMs(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.cleanup = [] + self.services = Services().services + self.zone = get_zone(self.apiclient, self.services) + return + + def tearDown(self): + try: + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + def test_01_list_sec_storage_vm(self): + """Test List secondary storage VMs + """ + + # Validate the following: + # 1. listSystemVM (systemvmtype=secondarystoragevm) + # should return only ONE SSVM per zone + # 2. The returned SSVM should be in Running state + # 3. listSystemVM for secondarystoragevm should list publicip, + # privateip and link-localip + # 4. The gateway programmed on the ssvm by listSystemVm should be + # the same as the gateway returned by listVlanIpRanges + # 5. DNS entries must match those given for the zone + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + #Verify SSVM response + self.assertNotEqual( + len(list_ssvm_response), + 0, + "Check list System VMs response" + ) + + list_zones_response = list_zones(self.apiclient) + + self.assertEqual( + isinstance(list_zones_response, list), + True, + "Check list response returns a valid list" + ) + + self.debug("Number of zones: %s" % len(list_zones_response)) + self.debug("Number of SSVMs: %s" % len(list_ssvm_response)) + # Number of Sec storage VMs = No of Zones + self.assertEqual( + len(list_ssvm_response), + len(list_zones_response), + "Check number of SSVMs with number of zones" + ) + #For each secondary storage VM check private IP, + #public IP, link local IP and DNS + for ssvm in list_ssvm_response: + + self.debug("SSVM state: %s" % ssvm.state) + self.assertEqual( + ssvm.state, + 'Running', + "Check whether state of SSVM is running" + ) + + self.assertEqual( + hasattr(ssvm, 'privateip'), + True, + "Check whether SSVM has private IP field" + ) + + self.assertEqual( + hasattr(ssvm, 'linklocalip'), + True, + "Check whether SSVM has link local IP field" + ) + + self.assertEqual( + hasattr(ssvm, 'publicip'), + True, + "Check whether SSVM has public IP field" + ) + + #Fetch corresponding ip ranges information from listVlanIpRanges + ipranges_response = list_vlan_ipranges( + self.apiclient, + zoneid=ssvm.zoneid + ) + self.assertEqual( + isinstance(ipranges_response, list), + True, + "Check list response returns a valid list" + ) + iprange = ipranges_response[0] + + self.assertEqual( + ssvm.gateway, + iprange.gateway, + "Check gateway with that of corresponding ip range" + ) + + #Fetch corresponding zone information from listZones + zone_response = list_zones( + self.apiclient, + id=ssvm.zoneid + ) + self.assertEqual( + isinstance(zone_response, list), + True, + "Check list response returns a valid list" + ) + self.assertEqual( + ssvm.dns1, + zone_response[0].dns1, + "Check DNS1 with that of corresponding zone" + ) + + self.assertEqual( + ssvm.dns2, + zone_response[0].dns2, + "Check DNS2 with that of corresponding zone" + ) + return + + def test_02_list_cpvm_vm(self): + """Test List console proxy VMs + """ + + # Validate the following: + # 1. listSystemVM (systemvmtype=consoleproxy) should return + # at least ONE CPVM per zone + # 2. The returned ConsoleProxyVM should be in Running state + # 3. listSystemVM for console proxy should list publicip, privateip + # and link-localip + # 4. The gateway programmed on the console proxy should be the same + # as the gateway returned by listZones + # 5. DNS entries must match those given for the zone + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + state='Running', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + "Check list response returns a valid list" + ) + #Verify CPVM response + self.assertNotEqual( + len(list_cpvm_response), + 0, + "Check list System VMs response" + ) + list_zones_response = list_zones(self.apiclient) + # Number of Console Proxy VMs = No of Zones + + self.assertEqual( + isinstance(list_zones_response, list), + True, + "Check list response returns a valid list" + ) + + self.debug("Number of zones: %s" % len(list_zones_response)) + self.debug("Number of CPVMs: %s" % len(list_cpvm_response)) + + self.assertEqual( + len(list_cpvm_response), + len(list_zones_response), + "Check number of CPVMs with number of zones" + ) + #For each CPVM check private IP, public IP, link local IP and DNS + for cpvm in list_cpvm_response: + + self.debug("CPVM state: %s" % cpvm.state) + self.assertEqual( + cpvm.state, + 'Running', + "Check whether state of CPVM is running" + ) + + self.assertEqual( + hasattr(cpvm, 'privateip'), + True, + "Check whether CPVM has private IP field" + ) + + self.assertEqual( + hasattr(cpvm, 'linklocalip'), + True, + "Check whether CPVM has link local IP field" + ) + + self.assertEqual( + hasattr(cpvm, 'publicip'), + True, + "Check whether CPVM has public IP field" + ) + #Fetch corresponding ip ranges information from listVlanIpRanges + ipranges_response = list_vlan_ipranges( + self.apiclient, + zoneid=cpvm.zoneid + ) + self.assertEqual( + isinstance(ipranges_response, list), + True, + "Check list response returns a valid list" + ) + iprange = ipranges_response[0] + + self.assertEqual( + cpvm.gateway, + iprange.gateway, + "Check gateway with that of corresponding ip range" + ) + + #Fetch corresponding zone information from listZones + zone_response = list_zones( + self.apiclient, + id=cpvm.zoneid + ) + + self.assertEqual( + cpvm.dns1, + zone_response[0].dns1, + "Check DNS1 with that of corresponding zone" + ) + + self.assertEqual( + cpvm.dns2, + zone_response[0].dns2, + "Check DNS2 with that of corresponding zone" + ) + return + + def test_03_ssvm_internals(self): + """Test SSVM Internals""" + + # Validate the following + # 1. The SSVM check script should not return any + # WARN|ERROR|FAIL messages + # 2. If you are unable to login to the SSVM with the signed key + # then test is deemed a failure + # 3. There should be only one ""cloud"" process running within the SSVM + # 4. If no process is running/multiple process are running + # then the test is a failure + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + ssvm = list_ssvm_response[0] + + hosts = list_hosts( + self.apiclient, + id=ssvm.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + self.debug("Running SSVM check script") + + result = get_process_status( + host.ipaddress, + self.services['host']["publicport"], + self.services['host']["username"], + self.services['host']["password"], + ssvm.linklocalip, + "/usr/local/cloud/systemvm/ssvm-check.sh |grep -e ERROR -e WARNING -e FAIL" + ) + res = str(result) + self.debug("SSVM script output: %s" % res) + + self.assertEqual( + res.count("ERROR"), + 1, + "Check for Errors in tests" + ) + + self.assertEqual( + res.count("WARNING"), + 1, + "Check for warnings in tests" + ) + + #Check status of cloud service + result = get_process_status( + host.ipaddress, + self.services['host']["publicport"], + self.services['host']["username"], + self.services['host']["password"], + ssvm.linklocalip, + "service cloud status" + ) + res = str(result) + self.debug("Cloud Process status: %s" % res) + # cloud.com service (type=secstorage) is running: process id: 2346 + self.assertEqual( + res.count("is running"), + 1, + "Check cloud service is running or not" + ) + return + + def test_04_cpvm_internals(self): + """Test CPVM Internals""" + + # Validate the following + # 1. test that telnet access on 8250 is available to + # the management server for the CPVM + # 2. No telnet access, test FAIL + # 3. Service cloud status should report cloud agent status to be + # running + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + state='Running', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + "Check list response returns a valid list" + ) + cpvm = list_cpvm_response[0] + + hosts = list_hosts( + self.apiclient, + id=cpvm.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + try: + telnet = telnetlib.Telnet( + str(self.apiclient.connection.mgtSvr), + '8250' + ) + self.debug("Telnet management server (IP: %s)" % + self.apiclient.connection.mgtSvr) + except Exception as e: + self.fail( + "Telnet Access failed for %s: %s" % \ + (self.apiclient.connection.mgtSvr, e) + ) + + self.debug("Checking cloud process status") + + result = get_process_status( + host.ipaddress, + self.services['host']["publicport"], + self.services['host']["username"], + self.services['host']["password"], + cpvm.linklocalip, + "service cloud status" + ) + res = str(result) + self.debug("Cloud Process status: %s" % res) + self.assertEqual( + res.count("is running"), + 1, + "Check cloud service is running or not" + ) + return + + def test_05_stop_ssvm(self): + """Test stop SSVM + """ + + # Validate the following + # 1. The SSVM should go to stop state + # 2. After a brief delay of say one minute, the SSVM should be + # restarted once again and return to Running state with previous two + # test cases still passing + # 3. If either of the two above steps fail the test is a failure + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + ssvm = list_ssvm_response[0] + + hosts = list_hosts( + self.apiclient, + id=ssvm.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + self.debug("Stopping SSVM: %s" % ssvm.id) + cmd = stopSystemVm.stopSystemVmCmd() + cmd.id = ssvm.id + self.apiclient.stopSystemVm(cmd) + + # Sleep to ensure that VM is in proper state + time.sleep(self.services["sleep"]) + + timeout = self.services["timeout"] + while True: + list_ssvm_response = list_ssvms( + self.apiclient, + id=ssvm.id + ) + if isinstance(list_ssvm_response, list): + if list_ssvm_response[0].state == 'Running': + break + elif timeout == 0: + raise Exception("List SSVM call failed!") + + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + ssvm_response = list_ssvm_response[0] + self.debug("SSVM state after debug: %s" % ssvm_response.state) + self.assertEqual( + ssvm_response.state, + 'Running', + "Check whether SSVM is running or not" + ) + # Call above tests to ensure SSVM is properly running + self.test_01_list_sec_storage_vm() + self.test_03_ssvm_internals() + return + + def test_06_stop_cpvm(self): + """Test stop CPVM + """ + + # Validate the following + # 1. The CPVM should go to stop state + # 2. After a brief delay of say one minute, the SSVM should be + # restarted once again and return to Running state with previous + # two test cases still passing + # 3. If either of the two above steps fail the test is a failure + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + state='Running', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + "Check list response returns a valid list" + ) + cpvm = list_cpvm_response[0] + + hosts = list_hosts( + self.apiclient, + id=cpvm.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + self.debug("Stopping CPVM: %s" % cpvm.id) + cmd = stopSystemVm.stopSystemVmCmd() + cmd.id = cpvm.id + self.apiclient.stopSystemVm(cmd) + + # Sleep to ensure that VM is in proper state + time.sleep(self.services["sleep"]) + + timeout = self.services["timeout"] + while True: + list_cpvm_response = list_ssvms( + self.apiclient, + id=cpvm.id + ) + if isinstance(list_cpvm_response, list): + if list_cpvm_response[0].state == 'Running': + break + elif timeout == 0: + raise Exception("List CPVM call failed!") + + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + + cpvm_response = list_cpvm_response[0] + + self.debug("CPVM state after debug: %s" % cpvm_response.state) + + self.assertEqual( + cpvm_response.state, + 'Running', + "Check whether CPVM is running or not" + ) + # Call above tests to ensure CPVM is properly running + self.test_02_list_cpvm_vm() + self.test_04_cpvm_internals() + return + + def test_07_reboot_ssvm(self): + """Test reboot SSVM + """ + # Validate the following + # 1. The SSVM should go to stop and return to Running state + # 2. SSVM's public-ip and private-ip must remain the same + # before and after reboot + # 3. The cloud process should still be running within the SSVM + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + zoneid=self.zone.id + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + + ssvm_response = list_ssvm_response[0] + + hosts = list_hosts( + self.apiclient, + id=ssvm_response.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + #Store the public & private IP values before reboot + old_public_ip = ssvm_response.publicip + old_private_ip = ssvm_response.privateip + + self.debug("Rebooting SSVM: %s" % ssvm_response.id) + cmd = rebootSystemVm.rebootSystemVmCmd() + cmd.id = ssvm_response.id + self.apiclient.rebootSystemVm(cmd) + + # Sleep to ensure that VM is in proper state + time.sleep(self.services["sleep"]) + + timeout = self.services["timeout"] + while True: + list_ssvm_response = list_ssvms( + self.apiclient, + id=ssvm_response.id + ) + if isinstance(list_ssvm_response, list): + if list_ssvm_response[0].state == 'Running': + break + elif timeout == 0: + raise Exception("List SSVM call failed!") + + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + + ssvm_response = list_ssvm_response[0] + self.debug("SSVM State: %s" % ssvm_response.state) + self.assertEqual( + 'Running', + str(ssvm_response.state), + "Check whether CPVM is running or not" + ) + + self.assertEqual( + ssvm_response.publicip, + old_public_ip, + "Check Public IP after reboot with that of before reboot" + ) + + self.assertEqual( + ssvm_response.privateip, + old_private_ip, + "Check Private IP after reboot with that of before reboot" + ) + #Call to verify cloud process is running + self.test_03_ssvm_internals() + return + + def test_08_reboot_cpvm(self): + """Test reboot CPVM + """ + # Validate the following + # 1. The CPVM should go to stop and return to Running state + # 2. CPVM's public-ip and private-ip must remain + # the same before and after reboot + # 3. the cloud process should still be running within the CPVM + + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + state='Running', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + "Check list response returns a valid list" + ) + cpvm_response = list_cpvm_response[0] + + hosts = list_hosts( + self.apiclient, + id=cpvm_response.hostid + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + #Store the public & private IP values before reboot + old_public_ip = cpvm_response.publicip + old_private_ip = cpvm_response.privateip + + self.debug("Rebooting CPVM: %s" % cpvm_response.id) + + cmd = rebootSystemVm.rebootSystemVmCmd() + cmd.id = cpvm_response.id + self.apiclient.rebootSystemVm(cmd) + + # Sleep to ensure that VM is in proper state + time.sleep(self.services["sleep"]) + + timeout = self.services["timeout"] + while True: + list_cpvm_response = list_ssvms( + self.apiclient, + id=cpvm_response.id + ) + if isinstance(list_cpvm_response, list): + if list_cpvm_response[0].state == 'Running': + break + elif timeout == 0: + raise Exception("List CPVM call failed!") + + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + + cpvm_response = list_cpvm_response[0] + + self.debug("CPVM state: %s" % cpvm_response.state) + self.assertEqual( + 'Running', + str(cpvm_response.state), + "Check whether CPVM is running or not" + ) + + self.assertEqual( + cpvm_response.publicip, + old_public_ip, + "Check Public IP after reboot with that of before reboot" + ) + + self.assertEqual( + cpvm_response.privateip, + old_private_ip, + "Check Private IP after reboot with that of before reboot" + ) + #Call to verify cloud process is running + self.test_04_cpvm_internals() + return + + def test_09_destroy_ssvm(self): + """Test destroy SSVM + """ + + # Validate the following + # 1. SSVM should be completely destroyed and a new one will spin up + # 2. listSystemVMs will show a different name for the + # systemVM from what it was before + # 3. new SSVM will have a public/private and link-local-ip + # 4. cloud process within SSVM must be up and running + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + state='Running', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + ssvm_response = list_ssvm_response[0] + + old_name = ssvm_response.name + + self.debug("Destroying SSVM: %s" % ssvm_response.id) + cmd = destroySystemVm.destroySystemVmCmd() + cmd.id = ssvm_response.id + self.apiclient.destroySystemVm(cmd) + + # Sleep to ensure that VM is in proper state + time.sleep(self.services["sleep"]) + + timeout = self.services["timeout"] + while True: + list_ssvm_response = list_ssvms( + self.apiclient, + zoneid=self.zone.id, + systemvmtype='secondarystoragevm' + ) + if isinstance(list_ssvm_response, list): + if list_ssvm_response[0].state == 'Running': + break + elif timeout == 0: + raise Exception("List SSVM call failed!") + + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + + ssvm_response = list_ssvm_response[0] + + # Verify Name, Public IP, Private IP and Link local IP + # for newly created SSVM + self.assertNotEqual( + ssvm_response.name, + old_name, + "Check SSVM new name with name of destroyed SSVM" + ) + self.assertEqual( + hasattr(ssvm_response, 'privateip'), + True, + "Check whether SSVM has private IP field" + ) + + self.assertEqual( + hasattr(ssvm_response, 'linklocalip'), + True, + "Check whether SSVM has link local IP field" + ) + + self.assertEqual( + hasattr(ssvm_response, 'publicip'), + True, + "Check whether SSVM has public IP field" + ) + + #Call to verify cloud process is running + self.test_03_ssvm_internals() + return + + def test_10_destroy_cpvm(self): + """Test destroy CPVM + """ + + # Validate the following + # 1. CPVM should be completely destroyed and a new one will spin up + # 2. listSystemVMs will show a different name for the systemVM from + # what it was before + # 3. new CPVM will have a public/private and link-local-ip + # 4. cloud process within CPVM must be up and running + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + zoneid=self.zone.id + ) + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + "Check list response returns a valid list" + ) + cpvm_response = list_cpvm_response[0] + + old_name = cpvm_response.name + + self.debug("Destroying CPVM: %s" % cpvm_response.id) + cmd = destroySystemVm.destroySystemVmCmd() + cmd.id = cpvm_response.id + self.apiclient.destroySystemVm(cmd) + + # Sleep to ensure that VM is in proper state + time.sleep(self.services["sleep"]) + + timeout = self.services["timeout"] + while True: + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + zoneid=self.zone.id + ) + if isinstance(list_cpvm_response, list): + if list_cpvm_response[0].state == 'Running': + break + elif timeout == 0: + raise Exception("List CPVM call failed!") + + time.sleep(self.services["sleep"]) + timeout = timeout - 1 + + cpvm_response = list_cpvm_response[0] + + # Verify Name, Public IP, Private IP and Link local IP + # for newly created CPVM + self.assertNotEqual( + cpvm_response.name, + old_name, + "Check SSVM new name with name of destroyed CPVM" + ) + self.assertEqual( + hasattr(cpvm_response, 'privateip'), + True, + "Check whether CPVM has private IP field" + ) + + self.assertEqual( + hasattr(cpvm_response, 'linklocalip'), + True, + "Check whether CPVM has link local IP field" + ) + + self.assertEqual( + hasattr(cpvm_response, 'publicip'), + True, + "Check whether CPVM has public IP field" + ) + + #Call to verify cloud process is running + self.test_04_cpvm_internals() + return diff --git a/test/integration/smoke/test_templates.py b/test/integration/smoke/test_templates.py new file mode 100644 index 00000000000..0dd873cd623 --- /dev/null +++ b/test/integration/smoke/test_templates.py @@ -0,0 +1,753 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Templates ISO +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +import urllib +from random import random +#Import System modules +import datetime + + +class Services: + """Test Templates Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "virtual_machine": { + "displayname": "testVM", + "hypervisor": 'XenServer', + "protocol": 'TCP', + "ssh_port": 22, + "username": "root", + "password": "password", + "privateport": 22, + "publicport": 22, + }, + "volume": { + "diskname": "Test Volume", + }, + "template_1": { + "displaytext": "Cent OS Template", + "name": "Cent OS Template", + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + }, + "template_2": { + "displaytext": "Public Template", + "name": "Public template", + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "isfeatured": True, + "ispublic": True, + "isextractable": True, + "mode": "HTTP_DOWNLOAD", + }, + "templatefilter": 'self', + "destzoneid": 5, + # For Copy template (Destination zone) + "isfeatured": True, + "ispublic": True, + "isextractable": False, + "bootable": True, + "passwordenabled": True, + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "mode": 'advanced', + # Networking mode: Advanced, basic + "sleep": 30, + "timeout": 10, + } + + +class TestCreateTemplate(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + return + + @classmethod + def setUpClass(cls): + cls.services = Services().services + cls.api_client = super(TestCreateTemplate, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["volume"]["diskoffering"] = cls.disk_offering.id + cls.services["volume"]["zoneid"] = cls.zone.id + cls.services["sourcezoneid"] = cls.zone.id + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + #create virtual machine + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + + #Stop virtual machine + cls.virtual_machine.stop(cls.api_client) + + # Poll listVM to ensure VM is stopped properly + timeout = cls.services["timeout"] + while True: + time.sleep(cls.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + cls.api_client, + id=cls.virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Stopped': + break + + if timeout == 0: + raise Exception( + "Failed to stop VM (ID: %s) in change service offering" % + vm.id) + + timeout = timeout - 1 + + list_volume = list_volumes( + cls.api_client, + virtualmachineid=cls.virtual_machine.id, + type='ROOT', + listall=True + ) + + cls.volume = list_volume[0] + cls._cleanup = [ + cls.account, + cls.service_offering, + cls.disk_offering, + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestCreateTemplate, cls).getClsTestClient().getApiClient() + #Cleanup resources used + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_01_create_template(self): + """Test create public & private template + """ + + # Validate the following: + # 1. database (vm_template table) should be updated + # with newly created template + # 2. UI should show the newly added template + # 3. ListTemplates API should show the newly added template + + #Create template from Virtual machine and Volume ID + template = Template.create( + self.apiclient, + self.services["template_1"], + self.volume.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.cleanup.append(template) + + self.debug("Created template with ID: %s" % template.id) + + list_template_response = list_templates( + self.apiclient, + templatefilter=\ + self.services["templatefilter"], + id=template.id + ) + + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check list response returns a valid list" + ) + #Verify template response to check whether template added successfully + self.assertNotEqual( + len(list_template_response), + 0, + "Check template available in List Templates" + ) + template_response = list_template_response[0] + + self.assertEqual( + template_response.displaytext, + self.services["template_1"]["displaytext"], + "Check display text of newly created template" + ) + name = template_response.name + self.assertEqual( + name.count(self.services["template_1"]["name"]), + 1, + "Check name of newly created template" + ) + self.assertEqual( + template_response.ostypeid, + self.services["template_1"]["ostypeid"], + "Check osTypeID of newly created template" + ) + return + + +class TestTemplates(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.services = Services().services + cls.api_client = super(TestTemplates, cls).getClsTestClient().getApiClient() + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["virtual_machine"]["zoneid"] = cls.zone.id + cls.services["volume"]["diskoffering"] = cls.disk_offering.id + cls.services["volume"]["zoneid"] = cls.zone.id + cls.services["template_2"]["zoneid"] = cls.zone.id + cls.services["sourcezoneid"] = cls.zone.id + + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + + cls.user = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + #create virtual machine + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["virtual_machine"], + templateid=template.id, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + #Stop virtual machine + cls.virtual_machine.stop(cls.api_client) + + # Poll listVM to ensure VM is stopped properly + timeout = cls.services["timeout"] + while True: + time.sleep(cls.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + cls.api_client, + id=cls.virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Stopped': + break + + if timeout == 0: + raise Exception( + "Failed to stop VM (ID: %s) in change service offering" % + vm.id) + + timeout = timeout - 1 + + list_volume = list_volumes( + cls.api_client, + virtualmachineid=cls.virtual_machine.id, + type='ROOT', + listall=True + ) + try: + cls.volume = list_volume[0] + except Exception as e: + raise Exception( + "Exception: Unable to find root volume foe VM: %s" % + cls.virtual_machine.id) + + #Create templates for Edit, Delete & update permissions testcases + cls.template_1 = Template.create( + cls.api_client, + cls.services["template_1"], + cls.volume.id, + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls.template_2 = Template.create( + cls.api_client, + cls.services["template_2"], + cls.volume.id, + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account, + cls.user + ] + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestTemplates, cls).getClsTestClient().getApiClient() + #Cleanup created resources such as templates and VMs + cleanup_resources(cls.api_client, cls._cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + try: + + self.dbclient.close() + #Clean up, terminate the created templates + cleanup_resources(self.apiclient, self.cleanup) + + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + return + + def test_02_edit_template(self): + """Test Edit template + """ + + # Validate the following: + # 1. UI should show the edited values for template + # 2. database (vm_template table) should have updated values + + new_displayText = random_gen() + new_name = random_gen() + + cmd = updateTemplate.updateTemplateCmd() + # Update template attributes + cmd.id = self.template_1.id + cmd.displaytext = new_displayText + cmd.name = new_name + cmd.bootable = self.services["bootable"] + cmd.passwordenabled = self.services["passwordenabled"] + + self.apiclient.updateTemplate(cmd) + + self.debug("Edited template with new name: %s" % new_name) + + # Sleep to ensure update reflected across all the calls + time.sleep(self.services["sleep"]) + + timeout = self.services["timeout"] + while True: + # Verify template response for updated attributes + list_template_response = list_templates( + self.apiclient, + templatefilter=\ + self.services["templatefilter"], + id=self.template_1.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + if isinstance(list_template_response, list): + break + elif timeout == 0: + raise Exception("List Template failed!") + + time.sleep(10) + timeout = timeout -1 + + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_template_response), + 0, + "Check template available in List Templates" + ) + template_response = list_template_response[0] + + self.debug("New Name: %s" % new_displayText) + self.debug("Name in Template response: %s" + % template_response.displaytext) + self.assertEqual( + template_response.displaytext, + new_displayText, + "Check display text of updated template" + ) + self.assertEqual( + template_response.name, + new_name, + "Check name of updated template" + ) + self.assertEqual( + str(template_response.passwordenabled).lower(), + str(self.services["passwordenabled"]).lower(), + "Check passwordenabled field of updated template" + ) + self.assertEqual( + template_response.ostypeid, + self.services["ostypeid"], + "Check OSTypeID of updated template" + ) + return + + def test_03_delete_template(self): + """Test delete template + """ + + # Validate the following: + # 1. UI should not show the deleted template + # 2. database (vm_template table) should not contain deleted template + + self.debug("Deleting Template ID: %s" % self.template_1.id) + + self.template_1.delete(self.apiclient) + + list_template_response = list_templates( + self.apiclient, + templatefilter=\ + self.services["templatefilter"], + id=self.template_1.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + # Verify template is deleted properly using ListTemplates + self.assertEqual( + list_template_response, + None, + "Check if template exists in List Templates" + ) + return + + def test_04_extract_template(self): + "Test for extract template" + + # Validate the following + # 1. Admin should able extract and download the templates + # 2. ListTemplates should display all the public templates + # for all kind of users + # 3 .ListTemplates should not display the system templates + + self.debug("Extracting template with ID: %s" % self.template_2.id) + + cmd = extractTemplate.extractTemplateCmd() + cmd.id = self.template_2.id + cmd.mode = self.services["template_2"]["mode"] + cmd.zoneid = self.zone.id + list_extract_response = self.apiclient.extractTemplate(cmd) + + try: + # Format URL to ASCII to retrieve response code + formatted_url = urllib.unquote_plus(list_extract_response.url) + url_response = urllib.urlopen(formatted_url) + response_code = url_response.getcode() + + except Exception: + self.fail( + "Extract Template Failed with invalid URL %s (template id: %s)" \ + % (formatted_url, self.template_2.id) + ) + self.assertEqual( + list_extract_response.id, + self.template_2.id, + "Check ID of the extracted template" + ) + self.assertEqual( + list_extract_response.extractMode, + self.services["template_2"]["mode"], + "Check mode of extraction" + ) + self.assertEqual( + list_extract_response.zoneid, + self.services["template_2"]["zoneid"], + "Check zone ID of extraction" + ) + self.assertEqual( + response_code, + 200, + "Check for a valid response download URL" + ) + return + + def test_05_template_permissions(self): + """Update & Test for template permissions""" + + # Validate the following + # 1. listTemplatePermissions returns valid + # permissions set for template + # 2. permission changes should be reflected in vm_template + # table in database + + self.debug("Updating Template permissions ID:%s" % self.template_2.id) + + cmd = updateTemplatePermissions.updateTemplatePermissionsCmd() + # Update template permissions + cmd.id = self.template_2.id + cmd.isfeatured = self.services["isfeatured"] + cmd.ispublic = self.services["ispublic"] + cmd.isextractable = self.services["isextractable"] + self.apiclient.updateTemplatePermissions(cmd) + + list_template_response = list_templates( + self.apiclient, + templatefilter='featured', + id=self.template_2.id, + account=self.account.account.name, + domainid=self.account.account.domainid + ) + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check list response returns a valid list" + ) + # Verify template response for updated permissions for normal user + template_response = list_template_response[0] + + self.assertEqual( + template_response.id, + self.template_2.id, + "Check template ID" + ) + self.assertEqual( + template_response.ispublic, + int(True), + "Check ispublic permission of template" + ) + + self.assertNotEqual( + template_response.templatetype, + 'SYSTEM', + "ListTemplates should not list any system templates" + ) + return + + def test_06_copy_template(self): + """Test for copy template from one zone to another""" + + # Validate the following + # 1. copy template should be successful and + # secondary storage should contain new copied template. + + self.debug("Copy template from Zone: %s to %s" % ( + self.services["sourcezoneid"], + self.services["destzoneid"] + )) + cmd = copyTemplate.copyTemplateCmd() + cmd.id = self.template_2.id + cmd.destzoneid = self.services["destzoneid"] + cmd.sourcezoneid = self.services["sourcezoneid"] + self.apiclient.copyTemplate(cmd) + + # Verify template is copied to another zone using ListTemplates + list_template_response = list_templates( + self.apiclient, + templatefilter=\ + self.services["templatefilter"], + id=self.template_2.id, + zoneid=self.services["destzoneid"] + ) + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_template_response), + 0, + "Check template extracted in List Templates" + ) + + template_response = list_template_response[0] + self.assertEqual( + template_response.id, + self.template_2.id, + "Check ID of the downloaded template" + ) + self.assertEqual( + template_response.zoneid, + self.services["destzoneid"], + "Check zone ID of the copied template" + ) + + # Cleanup- Delete the copied template + cmd = deleteTemplate.deleteTemplateCmd() + cmd.id = template_response.id + cmd.zoneid = self.services["destzoneid"] + self.apiclient.deleteTemplate(cmd) + return + + def test_07_list_public_templates(self): + """Test only public templates are visible to normal user""" + + # Validate the following + # 1. ListTemplates should show only 'public' templates for normal user + + list_template_response = list_templates( + self.apiclient, + templatefilter='featured', + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_template_response), + 0, + "Check template available in List Templates" + ) + #Template response should list all 'public' templates + for template in list_template_response: + self.assertEqual( + template.ispublic, + True, + "ListTemplates should list only public templates" + ) + return + + def test_08_list_system_templates(self): + """Test System templates are not visible to normal user""" + + # Validate the following + # 1. ListTemplates should not show 'SYSTEM' templates for normal user + + list_template_response = list_templates( + self.apiclient, + templatefilter='featured', + account=self.user.account.name, + domainid=self.user.account.domainid + ) + self.assertEqual( + isinstance(list_template_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_template_response), + 0, + "Check template available in List Templates" + ) + + for template in list_template_response: + self.assertNotEqual( + template.templatetype, + 'SYSTEM', + "ListTemplates should not list any system templates" + ) + return diff --git a/test/integration/smoke/test_vm_life_cycle.py b/test/integration/smoke/test_vm_life_cycle.py new file mode 100644 index 00000000000..618583fcdab --- /dev/null +++ b/test/integration/smoke/test_vm_life_cycle.py @@ -0,0 +1,947 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Virtual Machine Life Cycle +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from marvin.remoteSSHClient import remoteSSHClient +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +#Import System modules +import time + +class Services: + """Test VM Life Cycle Services + """ + + def __init__(self): + self.services = { + "disk_offering":{ + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended in create account to + # ensure unique username generated each time + "password": "fr3sca", + }, + "small": + # Create a small virtual machine instance with disk offering + { + "displayname": "testserver", + "username": "root", # VM creds for SSH + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "medium": # Create a medium virtual machine instance + { + "displayname": "testserver", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "service_offerings": + { + "tiny": + { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "small": + { + # Small service offering ID to for change VM + # service offering from medium to small + "name": "Small Instance", + "displaytext": "Small Instance", + "cpunumber": 1, + "cpuspeed": 500, + "memory": 256 + }, + "medium": + { + # Medium service offering ID to for + # change VM service offering from small to medium + "name": "Medium Instance", + "displaytext": "Medium Instance", + "cpunumber": 1, + "cpuspeed": 1000, + "memory": 1024 + } + }, + "iso": # ISO settings for Attach/Detach ISO tests + { + "displaytext": "Test ISO", + "name": "testISO", + "url": "http://iso.linuxquestions.org/download/504/1819/http/gd4.tuwien.ac.at/dsl-4.4.10.iso", + # Source URL where ISO is located + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "mode": 'HTTP_DOWNLOAD', # Downloading existing ISO + }, + "diskdevice": '/dev/xvdd', + # Disk device where ISO is attached to instance + "mount_dir": "/mnt/tmp", + "sleep": 60, + "timeout": 10, + #Migrate VM to hostid + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + # CentOS 5.3 (64-bit) + "mode":'advanced', + } + + +class TestDeployVM(cloudstackTestCase): + + def setUp(self): + + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.services = Services().services + # Get Zone, Domain and templates + domain = get_domain(self.apiclient, self.services) + zone = get_zone(self.apiclient, self.services) + + template = get_template( + self.apiclient, + zone.id, + self.services["ostypeid"] + ) + # Set Zones and disk offerings + self.services["small"]["zoneid"] = zone.id + self.services["small"]["template"] = template.id + + self.services["medium"]["zoneid"] = zone.id + self.services["medium"]["template"] = template.id + self.services["iso"]["zoneid"] = zone.id + + # Create Account, VMs, NAT Rules etc + self.account = Account.create( + self.apiclient, + self.services["account"], + domainid=domain.id + ) + + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["tiny"] + ) + # Cleanup + self.cleanup = [ + self.service_offering, + self.account + ] + + def test_deploy_vm(self): + """Test Deploy Virtual Machine + """ + + # Validate the following: + # 1. Virtual Machine is accessible via SSH + # 2. listVirtualMachines returns accurate information + # 3. The Cloud Database contains the valid information + + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["small"], + accountid=self.account.account.name, + domainid=self.account.account.domainid, + serviceofferingid=self.service_offering.id + ) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine.id + ) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.virtual_machine.id + ) + + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + vm_response = list_vm_response[0] + + self.assertEqual( + + vm_response.id, + self.virtual_machine.id, + "Check virtual machine id in listVirtualMachines" + ) + + self.assertEqual( + vm_response.displayname, + self.virtual_machine.displayname, + "Check virtual machine displayname in listVirtualMachines" + ) + return + + def tearDown(self): + try: + cleanup_resources(self.apiclient, self.cleanup) + except Exception as e: + self.debug("Warning! Exception in tearDown: %s" % e) + + +class TestVMLifeCycle(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestVMLifeCycle, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + domain = get_domain(cls.api_client, cls.services) + zone = get_zone(cls.api_client, cls.services) + template = get_template( + cls.api_client, + zone.id, + cls.services["ostypeid"] + ) + # Set Zones and disk offerings + cls.services["small"]["zoneid"] = zone.id + cls.services["small"]["template"] = template.id + + cls.services["medium"]["zoneid"] = zone.id + cls.services["medium"]["template"] = template.id + cls.services["iso"]["zoneid"] = zone.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=domain.id + ) + + cls.small_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offerings"]["small"] + ) + + cls.medium_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offerings"]["medium"] + ) + #create small and large virtual machines + cls.small_virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["small"], + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.small_offering.id, + mode=cls.services["mode"] + ) + cls.medium_virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["medium"], + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.medium_offering.id, + mode=cls.services["mode"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services["small"], + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.small_offering.id, + mode=cls.services["mode"] + ) + cls._cleanup = [ + cls.small_offering, + cls.medium_offering, + cls.account + ] + + @classmethod + def tearDownClass(cls): + cls.api_client = super(TestVMLifeCycle, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + return + + def setUp(self): + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def tearDown(self): + #Clean up, terminate the created ISOs + cleanup_resources(self.apiclient, self.cleanup) + return + + def test_01_stop_vm(self): + """Test Stop Virtual Machine + """ + + # Validate the following + # 1. Should Not be able to login to the VM. + # 2. listVM command should return + # this VM.State of this VM should be ""Stopped"". + + self.debug("Stopping VM - ID: %s" % self.virtual_machine.id) + self.small_virtual_machine.stop(self.apiclient) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM available in List Virtual Machines" + ) + + self.assertEqual( + list_vm_response[0].state, + "Stopped", + "Check virtual machine is in stopped state" + ) + return + + def test_02_start_vm(self): + """Test Start Virtual Machine + """ + # Validate the following + # 1. listVM command should return this VM.State + # of this VM should be Running". + + self.debug("Starting VM - ID: %s" % self.virtual_machine.id) + self.small_virtual_machine.start(self.apiclient) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM avaliable in List Virtual Machines" + ) + + self.debug( + "Verify listVirtualMachines response for virtual machine: %s" \ + % self.small_virtual_machine.id + ) + self.assertEqual( + list_vm_response[0].state, + "Running", + "Check virtual machine is in running state" + ) + return + + def test_03_reboot_vm(self): + """Test Reboot Virtual Machine + """ + + # Validate the following + # 1. Should be able to login to the VM. + # 2. listVM command should return the deployed VM. + # State of this VM should be "Running" + + self.debug("Rebooting VM - ID: %s" % self.virtual_machine.id) + self.small_virtual_machine.reboot(self.apiclient) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM avaliable in List Virtual Machines" + ) + + self.assertEqual( + list_vm_response[0].state, + "Running", + "Check virtual machine is in running state" + ) + return + + def test_04_change_offering_small(self): + """Change Offering to a small capacity + """ + + # Validate the following + # 1. Log in to the Vm .We should see that the CPU and memory Info of + # this Vm matches the one specified for "Small" service offering. + # 2. Using listVM command verify that this Vm + # has Small service offering Id. + + self.debug("Stopping VM - ID: %s" % self.medium_virtual_machine.id) + + self.medium_virtual_machine.stop(self.apiclient) + + # Poll listVM to ensure VM is stopped properly + timeout = self.services["timeout"] + + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.medium_virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Stopped': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to stop VM (ID: %s) in change service offering" % vm.id) + + timeout = timeout - 1 + + self.debug("Change Service offering VM - ID: %s" % + self.medium_virtual_machine.id) + + cmd = changeServiceForVirtualMachine.changeServiceForVirtualMachineCmd() + cmd.id = self.medium_virtual_machine.id + cmd.serviceofferingid = self.small_offering.id + self.apiclient.changeServiceForVirtualMachine(cmd) + + self.debug("Starting VM - ID: %s" % self.medium_virtual_machine.id) + self.medium_virtual_machine.start(self.apiclient) + + # Poll listVM to ensure VM is started properly + timeout = self.services["timeout"] + + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in running state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.medium_virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Running': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to start VM (ID: %s) after changing service offering" % vm.id) + + timeout = timeout - 1 + + try: + ssh = self.medium_virtual_machine.get_ssh_client() + except Exception as e: + self.fail( + "SSH Access failed for %s: %s" % \ + (self.medium_virtual_machine.ipaddress, e) + ) + + cpuinfo = ssh.execute("cat /proc/cpuinfo") + + cpu_cnt = len([i for i in cpuinfo if "processor" in i]) + #'cpu MHz\t\t: 2660.499' + cpu_speed = [i for i in cpuinfo if "cpu MHz" in i ][0].split()[3] + + meminfo = ssh.execute("cat /proc/meminfo") + #MemTotal: 1017464 kB + total_mem = [i for i in meminfo if "MemTotal" in i][0].split()[1] + + self.debug( + "CPU count: %s, CPU Speed: %s, Mem Info: %s" % ( + cpu_cnt, + cpu_speed, + total_mem + )) + self.assertAlmostEqual( + int(cpu_cnt), + self.small_offering.cpunumber, + "Check CPU Count for small offering" + ) + + self.assertAlmostEqual( + list_vm_response[0].cpuspeed, + self.small_offering.cpuspeed, + "Check CPU Speed for small offering" + ) + self.assertAlmostEqual( + int(total_mem) / 1024, # In MBs + self.small_offering.memory, + "Check Memory(kb) for small offering" + ) + return + + def test_05_change_offering_medium(self): + """Change Offering to a medium capacity + """ + # Validate the following + # 1. Log in to the Vm .We should see that the CPU and memory Info of + # this Vm matches the one specified for "Medium" service offering. + # 2. Using listVM command verify that this Vm + # has Medium service offering Id. + + self.debug("Stopping VM - ID: %s" % self.small_virtual_machine.id) + self.small_virtual_machine.stop(self.apiclient) + + # Poll listVM to ensure VM is stopped properly + timeout = self.services["timeout"] + + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Stopped': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to stop VM (ID: %s) in change service offering" % vm.id) + + timeout = timeout - 1 + + self.debug("Change service offering VM - ID: %s" % + self.small_virtual_machine.id) + + cmd = changeServiceForVirtualMachine.changeServiceForVirtualMachineCmd() + cmd.id = self.small_virtual_machine.id + cmd.serviceofferingid = self.medium_offering.id + self.apiclient.changeServiceForVirtualMachine(cmd) + + self.debug("Starting VM - ID: %s" % self.small_virtual_machine.id) + self.small_virtual_machine.start(self.apiclient) + + # Poll listVM to ensure VM is started properly + timeout = self.services["timeout"] + + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in running state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Running': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to start VM (ID: %s) after changing service offering" % vm.id) + + timeout = timeout - 1 + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + + try: + ssh_client = self.small_virtual_machine.get_ssh_client() + except Exception as e: + self.fail( + "SSH Access failed for %s: %s" % \ + (self.small_virtual_machine.ipaddress, e) + ) + + cpuinfo = ssh_client.execute("cat /proc/cpuinfo") + + cpu_cnt = len([i for i in cpuinfo if "processor" in i]) + #'cpu MHz\t\t: 2660.499' + cpu_speed = [i for i in cpuinfo if "cpu MHz" in i][0].split()[3] + + meminfo = ssh_client.execute("cat /proc/meminfo") + #MemTotal: 1017464 kB + total_mem = [i for i in meminfo if "MemTotal" in i][0].split()[1] + + self.debug( + "CPU count: %s, CPU Speed: %s, Mem Info: %s" % ( + cpu_cnt, + cpu_speed, + total_mem + )) + self.assertAlmostEqual( + int(cpu_cnt), + self.medium_offering.cpunumber, + "Check CPU Count for medium offering" + ) + + self.assertAlmostEqual( + list_vm_response[0].cpuspeed, + self.medium_offering.cpuspeed, + "Check CPU Speed for medium offering" + ) + + self.assertAlmostEqual( + int(total_mem) / 1024, # In MBs + self.medium_offering.memory, + "Check Memory(kb) for medium offering" + ) + return + + def test_06_destroy_vm(self): + """Test destroy Virtual Machine + """ + + # Validate the following + # 1. Should not be able to login to the VM. + # 2. listVM command should return this VM.State + # of this VM should be "Destroyed". + + self.debug("Destroy VM - ID: %s" % self.small_virtual_machine.id) + self.small_virtual_machine.delete(self.apiclient) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM avaliable in List Virtual Machines" + ) + + self.assertEqual( + list_vm_response[0].state, + "Destroyed", + "Check virtual machine is in destroyed state" + ) + return + + def test_07_restore_vm(self): + """Test recover Virtual Machine + """ + + # Validate the following + # 1. listVM command should return this VM. + # State of this VM should be "Stopped". + # 2. We should be able to Start this VM successfully. + + self.debug("Recovering VM - ID: %s" % self.small_virtual_machine.id) + + cmd = recoverVirtualMachine.recoverVirtualMachineCmd() + cmd.id = self.small_virtual_machine.id + self.apiclient.recoverVirtualMachine(cmd) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_vm_response), + 0, + "Check VM avaliable in List Virtual Machines" + ) + + self.assertEqual( + list_vm_response[0].state, + "Stopped", + "Check virtual machine is in Stopped state" + ) + + return + + def test_08_migrate_vm(self): + """Test migrate VM + """ + # Validate the following + # 1. Should be able to login to the VM. + # 2. listVM command should return this VM.State of this VM + # should be "Running" and the host should be the host + # to which the VM was migrated to + + hosts = Host.list( + self.apiclient, + zoneid=self.medium_virtual_machine.zoneid, + type='Routing' + ) + + self.assertEqual( + isinstance(hosts, list), + True, + "Check the number of hosts in the zone" + ) + self.assertEqual( + len(hosts), + 2, + "Atleast 2 hosts should be present in a zone for VM migration" + ) + + # Find the host of VM and also the new host to migrate VM. + if self.medium_virtual_machine.hostid == hosts[0].id: + host = hosts[1] + else: + host = hosts[0] + + self.debug("Migrating VM-ID: %s to Host: %s" % ( + self.medium_virtual_machine.id, + host.id + )) + + cmd = migrateVirtualMachine.migrateVirtualMachineCmd() + cmd.hostid = host.id + cmd.virtualmachineid = self.medium_virtual_machine.id + self.apiclient.migrateVirtualMachine(cmd) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.medium_virtual_machine.id + ) + self.assertEqual( + isinstance(list_vm_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + list_vm_response, + None, + "Check virtual machine is listVirtualMachines" + ) + + vm_response = list_vm_response[0] + + self.assertEqual( + vm_response.id, + self.medium_virtual_machine.id, + "Check virtual machine ID of migrated VM" + ) + + self.assertEqual( + vm_response.hostid, + host.id, + "Check destination hostID of migrated VM" + ) + return + + def test_09_expunge_vm(self): + """Test destroy(expunge) Virtual Machine + """ + # Validate the following + # 1. listVM command should NOT return this VM any more. + + self.debug("Expunge VM-ID: %s" % self.small_virtual_machine.id) + + cmd = destroyVirtualMachine.destroyVirtualMachineCmd() + cmd.id = self.small_virtual_machine.id + self.apiclient.destroyVirtualMachine(cmd) + + config = list_configurations( + self.apiclient, + name='expunge.delay' + ) + + response = config[0] + # Wait for some time more than expunge.delay + time.sleep(int(response.value) * 2) + + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.small_virtual_machine.id + ) + self.assertEqual( + list_vm_response, + None, + "Check Expunged virtual machine is listVirtualMachines" + ) + return + + def test_10_attachAndDetach_iso(self): + """Test for detach ISO to virtual machine""" + + # Validate the following + # 1. Create ISO + # 2. Attach ISO to VM + # 3. Log in to the VM. + # 4. The device should be available for use + # 5. Detach ISO + # 6. Check the device is properly detached by logging into VM + + iso = Iso.create( + self.apiclient, + self.services["iso"], + account=self.account.account.name, + domainid=self.account.account.domainid + ) + + self.debug("Successfully created ISO with ID: %s" % iso.id) + try: + iso.download(self.apiclient) + except Exception as e: + self.fail("Exception while downloading ISO %s: %s"\ + % (iso.id, e)) + + self.debug("Attach ISO with ID: %s to VM ID: %s" % ( + iso.id, + self.virtual_machine.id + )) + #Attach ISO to virtual machine + cmd = attachIso.attachIsoCmd() + cmd.id = iso.id + cmd.virtualmachineid = self.virtual_machine.id + self.apiclient.attachIso(cmd) + + try: + ssh_client = self.virtual_machine.get_ssh_client() + + cmds = [ + "mkdir -p %s" % self.services["mount_dir"], + "mount -rt iso9660 %s %s" \ + % ( + self.services["diskdevice"], + self.services["mount_dir"] + ), + ] + + for c in cmds: + res = ssh_client.execute(c) + + self.assertEqual(res, [], "Check mount is successful or not") + + c = "fdisk -l|grep %s|head -1" % self.services["diskdevice"] + res = ssh_client.execute(c) + #Disk /dev/xvdd: 4393 MB, 4393723904 bytes + + except Exception as e: + self.fail("SSH failed for virtual machine: %s - %s" % + (self.virtual_machine.ipaddress, e)) + + # Res may contain more than one strings depending on environment + # Split strings to form new list which is used for assertion on ISO size + result = [] + for i in res: + for k in i.split(): + result.append(k) + + # Get ISO size + iso_response = list_isos( + self.apiclient, + id=iso.id + ) + self.assertEqual( + isinstance(iso_response, list), + True, + "Check list response returns a valid list" + ) + iso_size = iso_response[0].size + + self.assertEqual( + str(iso_size) in result, + True, + "Check size of the attached ISO" + ) + try: + #Unmount ISO + command = "umount %s" % self.services["mount_dir"] + ssh_client.execute(command) + + except Exception as e: + self.fail("SSH failed for virtual machine: %s - %s" % + (self.virtual_machine.ipaddress, e)) + + #Detach from VM + cmd = detachIso.detachIsoCmd() + cmd.virtualmachineid = self.virtual_machine.id + self.apiclient.detachIso(cmd) + + try: + res = ssh_client.execute(c) + + except Exception as e: + self.fail("SSH failed for virtual machine: %s - %s" % + (self.virtual_machine.ipaddress, e)) + + # Check if ISO is properly detached from VM (using fdisk) + result = self.services["diskdevice"] in str(res) + + self.assertEqual( + result, + False, + "Check if ISO is detached from virtual machine" + ) + return diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py new file mode 100644 index 00000000000..2b3ec594093 --- /dev/null +++ b/test/integration/smoke/test_volumes.py @@ -0,0 +1,517 @@ +# -*- encoding: utf-8 -*- +# Copyright 2012 Citrix Systems, Inc. Licensed under the +# Apache License, Version 2.0 (the "License"); you may not use this +# file except in compliance with the License. Citrix Systems, Inc. +# reserves all rights not expressly granted by 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. +# +# Automatically generated by addcopyright.py at 04/03/2012 +""" BVT tests for Volumes +""" +#Import Local Modules +import marvin +from marvin.cloudstackTestCase import * +from marvin.cloudstackAPI import * +from integration.lib.utils import * +from integration.lib.base import * +from integration.lib.common import * +from marvin.remoteSSHClient import remoteSSHClient +#Import System modules +import os +import urllib +import time +import tempfile + + +class Services: + """Test Volume Services + """ + + def __init__(self): + self.services = { + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "test", + # Random characters are appended for unique + # username + "password": "fr3sca", + }, + "service_offering": { + "name": "Tiny Instance", + "displaytext": "Tiny Instance", + "cpunumber": 1, + "cpuspeed": 100, # in MHz + "memory": 64, # In MBs + }, + "disk_offering": { + "displaytext": "Small", + "name": "Small", + "disksize": 1 + }, + "volume_offerings": { + 0: { + "diskname": "TestDiskServ", + }, + }, + "customdisksize": 1, # GBs + "username": "root", # Creds for SSH to VM + "password": "password", + "ssh_port": 22, + "diskname": "TestDiskServ", + "hypervisor": 'XenServer', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + "diskdevice": "/dev/xvdb", + "ostypeid": '5776c0d2-f331-42db-ba3a-29f1f8319bc9', + "mode": 'advanced', + "sleep": 60, + "timeout": 10, + } + + +class TestCreateVolume(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestCreateVolume, cls).getClsTestClient().getApiClient() + cls.services = Services().services + + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + cls.custom_disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"], + custom=True + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.services["template"] = template.id + cls.services["customdiskofferingid"] = cls.custom_disk_offering.id + + # Create VMs, NAT Rules etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.custom_disk_offering, + cls.account + ] + + def setUp(self): + + self.apiClient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + + def test_01_create_volume(self): + """Test Volume creation for all Disk Offerings (incl. custom) + """ + + # Validate the following + # 1. Create volumes from the different sizes + # 2. Verify the size of volume with actual size allocated + + self.volumes = [] + for k, v in self.services["volume_offerings"].items(): + volume = Volume.create( + self.apiClient, + v, + zoneid=self.zone.id, + account=self.account.account.name, + domainid=self.account.account.domainid, + diskofferingid=self.disk_offering.id + ) + self.debug("Created a volume with ID: %s" % volume.id) + self.volumes.append(volume) + + volume = Volume.create_custom_disk( + self.apiClient, + self.services, + account=self.account.account.name, + domainid=self.account.account.domainid, + ) + self.debug("Created a volume with custom offering: %s" % volume.id) + self.volumes.append(volume) + + #Attach a volume with different disk offerings + #and check the memory allocated to each of them + for volume in self.volumes: + list_volume_response = list_volumes( + self.apiClient, + id=volume.id + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + self.debug( + "Attaching volume (ID: %s) to VM (ID: %s)" % ( + volume.id, + self.virtual_machine.id + )) + self.virtual_machine.attach_volume( + self.apiClient, + volume + ) + try: + ssh = self.virtual_machine.get_ssh_client() + ssh.execute("reboot") + + except Exception as e: + self.fail("SSH access failed for VM %s - %s" % + (self.virtual_machine.ipaddress, e)) + + # Poll listVM to ensure VM is started properly + timeout = self.services["timeout"] + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in running state + list_vm_response = list_virtual_machines( + self.apiClient, + id=self.virtual_machine.id + ) + + if isinstance(list_vm_response, list): + + vm = list_vm_response[0] + if vm.state == 'Running': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to start VM (ID: %s) " % vm.id) + + timeout = timeout - 1 + + try: + ssh = self.virtual_machine.get_ssh_client( + reconnect=True + ) + c = "fdisk -l" + res = ssh.execute(c) + + except Exception as e: + self.fail("SSH access failed for VM: %s - %s" % + (self.virtual_machine.ipaddress, e)) + + # Disk /dev/sda doesn't contain a valid partition table + # Disk /dev/sda: 21.5 GB, 21474836480 bytes + result = str(res) + self.debug("fdisk result: %s" % result) + + self.assertEqual( + str(list_volume_response[0].size) in result, + True, + "Check if promised disk size actually available" + ) + self.virtual_machine.detach_volume(self.apiClient, volume) + + def tearDown(self): + #Clean up, terminate the created volumes + cleanup_resources(self.apiClient, self.cleanup) + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = super(TestCreateVolume, cls).getClsTestClient().getApiClient() + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + +class TestVolumes(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = super(TestVolumes, cls).getClsTestClient().getApiClient() + cls.services = Services().services + # Get Zone, Domain and templates + cls.domain = get_domain(cls.api_client, cls.services) + cls.zone = get_zone(cls.api_client, cls.services) + cls.disk_offering = DiskOffering.create( + cls.api_client, + cls.services["disk_offering"] + ) + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + cls.services["domainid"] = cls.domain.id + cls.services["zoneid"] = cls.zone.id + cls.services["template"] = template.id + cls.services["diskofferingid"] = cls.disk_offering.id + + # Create VMs, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"], + domainid=cls.domain.id + ) + + cls.services["account"] = cls.account.account.name + cls.service_offering = ServiceOffering.create( + cls.api_client, + cls.services["service_offering"] + ) + cls.virtual_machine = VirtualMachine.create( + cls.api_client, + cls.services, + accountid=cls.account.account.name, + domainid=cls.account.account.domainid, + serviceofferingid=cls.service_offering.id, + mode=cls.services["mode"] + ) + + cls.volume = Volume.create( + cls.api_client, + cls.services, + account=cls.account.account.name, + domainid=cls.account.account.domainid + ) + cls._cleanup = [ + cls.service_offering, + cls.disk_offering, + cls.account + ] + + @classmethod + def tearDownClass(cls): + try: + cleanup_resources(cls.api_client, cls._cleanup) + except Exception as e: + raise Exception("Warning: Exception during cleanup : %s" % e) + + def setUp(self): + self.apiClient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + + def test_02_attach_volume(self): + """Attach a created Volume to a Running VM + """ + # Validate the following + # 1. shows list of volumes + # 2. "Attach Disk" pop-up box will display with list of instances + # 3. disk should be attached to instance successfully + + self.debug( + "Attaching volume (ID: %s) to VM (ID: %s)" % ( + self.volume.id, + self.virtual_machine.id + )) + self.virtual_machine.attach_volume(self.apiClient, self.volume) + + list_volume_response = list_volumes( + self.apiClient, + id=self.volume.id + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + volume = list_volume_response[0] + self.assertNotEqual( + volume.virtualmachineid, + None, + "Check if volume state (attached) is reflected" + ) + try: + #Format the attached volume to a known fs + format_volume_to_ext3(self.virtual_machine.get_ssh_client()) + + except Exception as e: + + self.fail("SSH failed for VM: %s - %s" % + (self.virtual_machine.ipaddress, e)) + return + + def test_03_download_attached_volume(self): + """Download a Volume attached to a VM + """ + # Validate the following + # 1. download volume will fail with proper error message + # "Failed - Invalid state of the volume with ID: + # It should be either detached or the VM should be in stopped state + + self.debug("Extract attached Volume ID: %s" % self.volume.id) + + cmd = extractVolume.extractVolumeCmd() + cmd.id = self.volume.id + cmd.mode = "HTTP_DOWNLOAD" + cmd.zoneid = self.services["zoneid"] + # A proper exception should be raised; + # downloading attach VM is not allowed + with self.assertRaises(Exception): + self.apiClient.extractVolume(cmd) + + def test_04_delete_attached_volume(self): + """Delete a Volume attached to a VM + """ + + # Validate the following + # 1. delete volume will fail with proper error message + # "Failed - Invalid state of the volume with ID: + # It should be either detached or the VM should be in stopped state + + self.debug("Trying to delete attached Volume ID: %s" % + self.volume.id) + + cmd = deleteVolume.deleteVolumeCmd() + cmd.id = self.volume.id + #Proper exception should be raised; deleting attach VM is not allowed + #with self.assertRaises(Exception): + result = self.apiClient.deleteVolume(cmd) + self.assertEqual( + result, + None, + "Check for delete download error while volume is attached" + ) + + def test_05_detach_volume(self): + """Detach a Volume attached to a VM + """ + + # Validate the following + # Data disk should be detached from instance and detached data disk + # details should be updated properly + + self.debug( + "Detaching volume (ID: %s) from VM (ID: %s)" % ( + self.volume.id, + self.virtual_machine.id + )) + + self.virtual_machine.detach_volume(self.apiClient, self.volume) + #Sleep to ensure the current state will reflected in other calls + time.sleep(self.services["sleep"]) + list_volume_response = list_volumes( + self.apiClient, + id=self.volume.id + ) + self.assertEqual( + isinstance(list_volume_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + volume = list_volume_response[0] + self.assertEqual( + volume.virtualmachineid, + None, + "Check if volume state (detached) is reflected" + ) + return + + def test_06_download_detached_volume(self): + """Download a Volume unattached to an VM + """ + # Validate the following + # 1. able to download the volume when its not attached to instance + + self.debug("Extract detached Volume ID: %s" % self.volume.id) + + cmd = extractVolume.extractVolumeCmd() + cmd.id = self.volume.id + cmd.mode = "HTTP_DOWNLOAD" + cmd.zoneid = self.services["zoneid"] + extract_vol = self.apiClient.extractVolume(cmd) + + #Attempt to download the volume and save contents locally + try: + formatted_url = urllib.unquote_plus(extract_vol.url) + response = urllib.urlopen(formatted_url) + fd, path = tempfile.mkstemp() + os.close(fd) + fd = open(path, 'wb') + fd.write(response.read()) + fd.close() + + except Exception: + self.fail( + "Extract Volume Failed with invalid URL %s (vol id: %s)" \ + % (extract_vol.url, self.volume.id) + ) + + def test_07_delete_detached_volume(self): + """Delete a Volume unattached to an VM + """ + # Validate the following + # 1. volume should be deleted successfully and listVolume should not + # contain the deleted volume details. + # 2. "Delete Volume" menu item not shown under "Actions" menu. + # (UI should not allow to delete the volume when it is attached + # to instance by hiding the menu Item) + + self.debug("Delete Volume ID: %s" % self.volume.id) + + cmd = deleteVolume.deleteVolumeCmd() + cmd.id = self.volume.id + self.apiClient.deleteVolume(cmd) + + list_volume_response = list_volumes( + self.apiClient, + id=self.volume.id, + type='DATADISK' + ) + self.assertEqual( + list_volume_response, + None, + "Check if volume exists in ListVolumes" + ) + return \ No newline at end of file