From 5f2e35fa7015f2281dbe91d79673359f72d12837 Mon Sep 17 00:00:00 2001 From: Chirag Jog Date: Tue, 6 Mar 2012 09:18:13 -0800 Subject: [PATCH] Port the BVT tests from 2.2.y to Acton 3.0.x --- tools/testClient/__init__.py | 0 tools/testClient/testcase/BVT-tests/README | 41 + .../testClient/testcase/BVT-tests/__init__.py | 0 .../testcase/BVT-tests/test_disk_offerings.py | 204 ++ .../testcase/BVT-tests/test_hosts.py | 217 +++ .../testClient/testcase/BVT-tests/test_iso.py | 488 +++++ .../testcase/BVT-tests/test_network.py | 1654 +++++++++++++++++ .../BVT-tests/test_primary_storage.py | 231 +++ .../testcase/BVT-tests/test_routers.py | 788 ++++++++ .../BVT-tests/test_secondary_storage.py | 373 ++++ .../BVT-tests/test_service_offerings.py | 229 +++ .../testcase/BVT-tests/test_snapshots.py | 1123 +++++++++++ .../testcase/BVT-tests/test_ssvm.py | 884 +++++++++ .../testcase/BVT-tests/test_templates.py | 740 ++++++++ .../testcase/BVT-tests/test_vm_life_cycle.py | 915 +++++++++ .../testcase/BVT-tests/test_volumes.py | 505 +++++ tools/testClient/testcase/__init__.py | 0 tools/testClient/testcase/libs/__init__.py | 0 tools/testClient/testcase/libs/base.py | 1442 ++++++++++++++ tools/testClient/testcase/libs/common.py | 415 +++++ tools/testClient/testcase/libs/utils.py | 101 + 21 files changed, 10350 insertions(+) create mode 100644 tools/testClient/__init__.py create mode 100644 tools/testClient/testcase/BVT-tests/README create mode 100644 tools/testClient/testcase/BVT-tests/__init__.py create mode 100644 tools/testClient/testcase/BVT-tests/test_disk_offerings.py create mode 100644 tools/testClient/testcase/BVT-tests/test_hosts.py create mode 100644 tools/testClient/testcase/BVT-tests/test_iso.py create mode 100644 tools/testClient/testcase/BVT-tests/test_network.py create mode 100644 tools/testClient/testcase/BVT-tests/test_primary_storage.py create mode 100644 tools/testClient/testcase/BVT-tests/test_routers.py create mode 100644 tools/testClient/testcase/BVT-tests/test_secondary_storage.py create mode 100644 tools/testClient/testcase/BVT-tests/test_service_offerings.py create mode 100644 tools/testClient/testcase/BVT-tests/test_snapshots.py create mode 100644 tools/testClient/testcase/BVT-tests/test_ssvm.py create mode 100644 tools/testClient/testcase/BVT-tests/test_templates.py create mode 100644 tools/testClient/testcase/BVT-tests/test_vm_life_cycle.py create mode 100644 tools/testClient/testcase/BVT-tests/test_volumes.py create mode 100644 tools/testClient/testcase/__init__.py create mode 100644 tools/testClient/testcase/libs/__init__.py create mode 100644 tools/testClient/testcase/libs/base.py create mode 100644 tools/testClient/testcase/libs/common.py create mode 100644 tools/testClient/testcase/libs/utils.py diff --git a/tools/testClient/__init__.py b/tools/testClient/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/testClient/testcase/BVT-tests/README b/tools/testClient/testcase/BVT-tests/README new file mode 100644 index 00000000000..b4c5d82ac19 --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/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/tools/testClient/testcase/BVT-tests/__init__.py b/tools/testClient/testcase/BVT-tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/testClient/testcase/BVT-tests/test_disk_offerings.py b/tools/testClient/testcase/BVT-tests/test_disk_offerings.py new file mode 100644 index 00000000000..217c9f07325 --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_disk_offerings.py @@ -0,0 +1,204 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# + +""" BVT tests for Disk offerings""" + +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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 = fetch_api_client() + 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 = fetch_api_client() + 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 \ No newline at end of file diff --git a/tools/testClient/testcase/BVT-tests/test_hosts.py b/tools/testClient/testcase/BVT-tests/test_hosts.py new file mode 100644 index 00000000000..45a7db7f19d --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_hosts.py @@ -0,0 +1,217 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for Hosts and Clusters +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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", + }, + }, + "zoneid": '5894a264-12b0-4257-a316-06c831bcf8e2', + # Optional, if specified the mentioned zone will be + # used for tests + } + +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/tools/testClient/testcase/BVT-tests/test_iso.py b/tools/testClient/testcase/BVT-tests/test_iso.py new file mode 100644 index 00000000000..f12a06d87af --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_iso.py @@ -0,0 +1,488 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for Templates ISO +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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": '5a5b5f64-de08-452d-9672-608a4946f754', + }, + "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": '5a5b5f64-de08-452d-9672-608a4946f754', + "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": '5a5b5f64-de08-452d-9672-608a4946f754', + # CentOS 5.3 (64 bit) + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "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.zone = get_zone(self.apiclient, self.services) + self.services["iso_2"]["zoneid"] = self.zone.id + + self.account = Account.create( + self.apiclient, + self.services["account"] + ) + + 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 = fetch_api_client() + + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + 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"], + ) + 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 = fetch_api_client() + #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"] + cmd.ostypeid = self.services["ostypeid"] + + 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 \ No newline at end of file diff --git a/tools/testClient/testcase/BVT-tests/test_network.py b/tools/testClient/testcase/BVT-tests/test_network.py new file mode 100644 index 00000000000..1218b37af27 --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_network.py @@ -0,0 +1,1654 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# + +""" BVT tests for Network Life Cycle +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +import remoteSSHClient +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.common import * +#Import System modules +import time + + +class Services: + """Test Network Services + """ + + def __init__(self): + self.services = { + "ostypeid": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + # Cent OS 5.3 (64 bit) + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "mode": 'advanced', + # Networking mode: Basic or advanced + "lb_switch_wait": 10, + # Time interval after which LB switches the requests + "sleep": 60, + "timeout":10, + "network": { + "name": "Test Network", + "displaytext": "Test Network", + "networkoffering": '4c6dea7e-7aa8-4b17-bf1c-26c312586e7c', + }, + "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', + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "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 = fetch_api_client() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + + # Create Accounts & networks + cls.account = Account.create( + cls.api_client, + cls.services["account"], + admin=True + ) + + cls.user = Account.create( + cls.api_client, + cls.services["account"], + ) + cls.services["network"]["zoneid"] = cls.zone.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, + ] + 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 = fetch_api_client() + cls.services = Services().services + + # Get Zone, Domain and templates + 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 + ) + 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, + 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 = fetch_api_client() + 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 = fetch_api_client() + cls.services = Services().services + # Get Zone, Domain and templates + 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 + ) + 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, + serviceofferingid=cls.service_offering.id + ) + cls.vm_2 = VirtualMachine.create( + cls.api_client, + cls.services["server"], + templateid=template.id, + accountid=cls.account.account.name, + 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.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.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.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]) + + 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.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 + ) + 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, + 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.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 + ) + 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, + serviceofferingid=self.service_offering.id + ) + + self.vm_2 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + serviceofferingid=self.service_offering.id + ) + + self.vm_3 = VirtualMachine.create( + self.apiclient, + self.services["server"], + templateid=template.id, + accountid=self.account.account.name, + 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.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 + ) + + 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, + 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.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 + ) + 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, + 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 \ No newline at end of file diff --git a/tools/testClient/testcase/BVT-tests/test_primary_storage.py b/tools/testClient/testcase/BVT-tests/test_primary_storage.py new file mode 100644 index 00000000000..4c9e5bc18fd --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_primary_storage.py @@ -0,0 +1,231 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for Primary Storage +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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', + }, + }, + "zoneid": '5894a264-12b0-4257-a316-06c831bcf8e2', + # Optional, if specified the mentioned zone will be + # used for tests + } + +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/tools/testClient/testcase/BVT-tests/test_routers.py b/tools/testClient/testcase/BVT-tests/test_routers.py new file mode 100644 index 00000000000..de9e9f6d7c6 --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_routers.py @@ -0,0 +1,788 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for routers +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +import remoteSSHClient +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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', + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "account": { + "email": "test@test.com", + "firstname": "Test", + "lastname": "User", + "username": "testuser", + "password": "fr3sca", + }, + "ostypeid":'0c2c5d19-525b-41be-a8c3-c6607412f82b', + "sleep": 60, + "timeout": 10, + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "mode": 'advanced', #Networking mode: Basic, Advanced + } + + +class TestRouterServices(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + + cls.api_client = fetch_api_client() + cls.services = Services().services + # Get Zone, Domain and templates + 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"] + ) + 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, + serviceofferingid=cls.service_offering.id + ) + cls.cleanup = [ + cls.vm_1, + cls.account, + cls.service_offering + ] + return + + @classmethod + def tearDownClass(cls): + try: + cls.api_client = fetch_api_client() + #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' + ) + 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' + ) + 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' + ) + 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) + + config = list_configurations( + self.apiclient, + name='network.gc.interval' + ) + self.assertEqual( + isinstance(config, list), + True, + "Check list response returns a valid list" + ) + response = config[0] + + # Wait for network.gc.interval * 3 time + time.sleep(int(response.value) * 3) + + 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/tools/testClient/testcase/BVT-tests/test_secondary_storage.py b/tools/testClient/testcase/BVT-tests/test_secondary_storage.py new file mode 100644 index 00000000000..76719ad87fa --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_secondary_storage.py @@ -0,0 +1,373 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for Secondary Storage +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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, + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + } + +class TestSecStorageServices(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = fetch_api_client() + 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.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.services["domainid"] + ) + + # 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.services["domainid"] + ) + + 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.services["domainid"] + ) + + 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/tools/testClient/testcase/BVT-tests/test_service_offerings.py b/tools/testClient/testcase/BVT-tests/test_service_offerings.py new file mode 100644 index 00000000000..c9590393f0d --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_service_offerings.py @@ -0,0 +1,229 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# + +""" BVT tests for Service offerings""" + +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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 = fetch_api_client() + 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 = fetch_api_client() + #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/tools/testClient/testcase/BVT-tests/test_snapshots.py b/tools/testClient/testcase/BVT-tests/test_snapshots.py new file mode 100644 index 00000000000..e8ee666205a --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_snapshots.py @@ -0,0 +1,1123 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for Snapshots +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.common import * +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', + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + + "server_without_disk": + { + "displayname": "Test VM-No Disk", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "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": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + "templatefilter": 'self', + }, + "ostypeid": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + # Cent OS 5.3 (64 bit) + "diskdevice": "/dev/xvdb", # Data Disk + "rootdisk": "/dev/xvda", # Root Disk + + "diskname": "Test Disk", + "size": 1, # GBs + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + + "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, + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "sleep": 60, + "timeout": 10, + "mode": 'advanced', + # Networking mode, Advanced, Basic + } + + +class TestSnapshotRootDisk(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = fetch_api_client() + cls.services = Services().services + # Get Zone, Domain and templates + cls.zone = get_zone(cls.api_client, cls.services) + + template = get_template( + cls.api_client, + cls.zone.id, + cls.services["ostypeid"] + ) + 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"] + ) + + 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, + 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" + ) + + # hosts[0].name = "nfs://192.168.100.21/export/test" + parse_url = (hosts[0].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: + result = ssh_client.execute(c) + + except Exception: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine_with_disk.ipaddress) + + res = str(result) + # Check snapshot UUID in secondary storage and database + self.assertEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + # Unmount the Sec Storage + cmds = [ + "umount %s" % (self.services["mount_dir"]), + ] + try: + for c in cmds: + result = ssh_client.execute(c) + + except Exception as e: + self.fail("SSH failed for Virtual machine: %s" % + self.virtual_machine_with_disk.ipaddress) + + return + + +class TestSnapshots(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = fetch_api_client() + cls.services = Services().services + # Get Zone, Domain and templates + 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_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"] + ) + + 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, + 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, + 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" + ) + + # hosts[0].name = "nfs://192.168.100.21/export" + parse_url = (hosts[0].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) + + res = str(result) + # Check snapshot UUID in secondary storage and database + self.assertEqual( + res.count(snapshot_uuid), + 1, + "Check snapshot UUID in secondary storage and database" + ) + # 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.virtual_machine_with_disk.ipaddress) + + 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: + 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: + 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: + 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"] + )) + 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' + ) + + 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' + ) + + 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"] + ) + ] + + for c in cmds: + ssh_client.execute(c) + + 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: + 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, + 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"] + )) + 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 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: + 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/tools/testClient/testcase/BVT-tests/test_ssvm.py b/tools/testClient/testcase/BVT-tests/test_ssvm.py new file mode 100644 index 00000000000..5e0d9b01fc1 --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_ssvm.py @@ -0,0 +1,884 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for SSVM +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +import remoteSSHClient +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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, + }, + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "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' + ) + 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" + ) + # 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.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' + ) + 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.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.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 + + hosts = list_hosts( + self.apiclient, + zoneid=self.zone.id, + type='Routing', + state='Up' + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + hostid=host.id + ) + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + ssvm = list_ssvm_response[0] + + self.debug("Checking cloud process status") + + 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.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) + # 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 + + hosts = list_hosts( + self.apiclient, + zoneid=self.zone.id, + type='Routing', + state='Up' + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + hostid=host.id + ) + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + "Check list response returns a valid list" + ) + cpvm = list_cpvm_response[0] + + try: + telnet = telnetlib.Telnet( + str(self.apiclient.connection.mgtSvr), + '8250' + ) + 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.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 + + hosts = list_hosts( + self.apiclient, + zoneid=self.zone.id, + type='Routing', + state='Up' + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + hostid=host.id + ) + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + ssvm = list_ssvm_response[0] + + 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.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 + + hosts = list_hosts( + self.apiclient, + zoneid=self.zone.id, + type='Routing', + state='Up' + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + hostid=host.id + ) + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + "Check list response returns a valid list" + ) + cpvm = list_cpvm_response[0] + + 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.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 + + hosts = list_hosts( + self.apiclient, + zoneid=self.zone.id, + type='Routing', + state='Up' + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + list_ssvm_response = list_ssvms( + self.apiclient, + systemvmtype='secondarystoragevm', + hostid=host.id + ) + + self.assertEqual( + isinstance(list_ssvm_response, list), + True, + "Check list response returns a valid list" + ) + + ssvm_response = list_ssvm_response[0] + + #Store the public & private IP values before reboot + old_public_ip = ssvm_response.publicip + old_private_ip = ssvm_response.privateip + + 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.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 + + hosts = list_hosts( + self.apiclient, + zoneid=self.zone.id, + type='Routing', + state='Up' + ) + self.assertEqual( + isinstance(hosts, list), + True, + "Check list response returns a valid list" + ) + host = hosts[0] + + list_cpvm_response = list_ssvms( + self.apiclient, + systemvmtype='consoleproxy', + hostid=host.id + ) + self.assertEqual( + isinstance(list_cpvm_response, list), + True, + "Check list response returns a valid list" + ) + cpvm_response = list_cpvm_response[0] + + #Store the public & private IP values before reboot + old_public_ip = cpvm_response.publicip + old_private_ip = cpvm_response.privateip + + 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.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, + zoneid=self.zone.id, + systemvmtype='secondarystoragevm' + ) + 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 + + 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 + + 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/tools/testClient/testcase/BVT-tests/test_templates.py b/tools/testClient/testcase/BVT-tests/test_templates.py new file mode 100644 index 00000000000..59d2e1f1150 --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_templates.py @@ -0,0 +1,740 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for Templates ISO +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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', + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "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": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + }, + "template_2": { + "displaytext": "Public Template", + "name": "Public template", + "ostypeid": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + "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": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "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 = fetch_api_client() + + # Get Zone, Domain and templates + 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"] + ) + 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, + 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 = fetch_api_client() + #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 = fetch_api_client() + + # Get Zone, Domain and templates + 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"] + ) + + cls.user = Account.create( + cls.api_client, + cls.services["account"] + ) + + 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, + 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 = fetch_api_client() + #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"] + cmd.ostypeid = self.services["ostypeid"] + + 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/tools/testClient/testcase/BVT-tests/test_vm_life_cycle.py b/tools/testClient/testcase/BVT-tests/test_vm_life_cycle.py new file mode 100644 index 00000000000..c7f5f316f0b --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_vm_life_cycle.py @@ -0,0 +1,915 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# + +""" BVT tests for Virtual Machine Life Cycle +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +import remoteSSHClient +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.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', + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + }, + "medium": # Create a medium virtual machine instance + { + "displayname": "testserver", + "username": "root", + "password": "password", + "ssh_port": 22, + "hypervisor": 'XenServer', + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "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": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + "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, + "hostid": 5, + #Migrate VM to hostid + "ostypeid": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + # CentOS 5.3 (64-bit) + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "mode":'advanced', + # Networking mode: Basic or 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 + 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"] + ) + + 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, + 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 = fetch_api_client() + cls.services = Services().services + + # Get Zone, Domain and templates + 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"] + ) + + 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, + 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, + 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, + 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 = fetch_api_client() + 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 avaliable 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 + + self.debug("Migrating VM-ID: %s to Host: %s" % ( + self.medium_virtual_machine.id, + self.services["hostid"] + )) + + cmd = migrateVirtualMachine.migrateVirtualMachineCmd() + cmd.hostid = self.services["hostid"] + 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, + self.services["hostid"], + "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/tools/testClient/testcase/BVT-tests/test_volumes.py b/tools/testClient/testcase/BVT-tests/test_volumes.py new file mode 100644 index 00000000000..589367bffcd --- /dev/null +++ b/tools/testClient/testcase/BVT-tests/test_volumes.py @@ -0,0 +1,505 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# +""" BVT tests for Volumes +""" +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +from testcase.libs.utils import * +from testcase.libs.base import * +from testcase.libs.common import * +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", + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + }, + }, + "customdisksize": 1, # GBs + "username": "root", # Creds for SSH to VM + "password": "password", + "ssh_port": 22, + "diskname": "TestDiskServ", + "hypervisor": 'XenServer', + "domainid": '9ee36d2e-8b8f-432e-a927-a678ebec1d6b', + "privateport": 22, + "publicport": 22, + "protocol": 'TCP', + "diskdevice": "/dev/xvdb", + "ostypeid": '0c2c5d19-525b-41be-a8c3-c6607412f82b', + "zoneid": '4a6c0290-e64d-40fc-afbb-4a05cab6fa4b', + # Optional, if specified the mentioned zone will be + # used for tests + "mode": 'advanced', + "sleep": 60, + "timeout": 10, + } + + +class TestCreateVolume(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + cls.api_client = fetch_api_client() + cls.services = Services().services + + # Get Zone, Domain and templates + 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["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"] + ) + + 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, + 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 = fetch_api_client() + 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 = fetch_api_client() + cls.services = Services().services + # Get Zone, Domain and templates + 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["template"] = template.id + cls.services["diskofferingid"] = cls.disk_offering.id + + # Create VMs, VMs etc + cls.account = Account.create( + cls.api_client, + cls.services["account"] + ) + + 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, + 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 diff --git a/tools/testClient/testcase/__init__.py b/tools/testClient/testcase/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/testClient/testcase/libs/__init__.py b/tools/testClient/testcase/libs/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tools/testClient/testcase/libs/base.py b/tools/testClient/testcase/libs/base.py new file mode 100644 index 00000000000..65f3c1ecf4d --- /dev/null +++ b/tools/testClient/testcase/libs/base.py @@ -0,0 +1,1442 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# + +""" Base class for all Cloudstack resources + -Virtual machine, Volume, Snapshot etc +""" + +from utils import is_server_ssh_ready, random_gen +from 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, 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"] + + # List Networks for that user + command = listNetworks.listNetworksCmd() + command.zoneid = services["zoneid"] + command.account = accountid or services["account"] + command.domainid = domainid or services["domainid"] + network = apiclient.listNetworks(command) + + if networkids: + cmd.networkids = networkids + elif "networkids" in services: + cmd.networkids = services["networkids"] +# elif network: #If user already has source NAT created, then use that +# if hasattr(network[0], "account"): +# cmd.networkids = str(network[0].id) + + 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"]) + + 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): + """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 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): + """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"] + + 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): + """Create Snapshot""" + cmd = createSnapshot.createSnapshotCmd() + cmd.volumeid = volume_id + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + 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): + """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 + + return Template(apiclient.createTemplate(cmd).__dict__) + + @classmethod + def register(cls, apiclient, services, 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"] + 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): + """Download Template""" + #Sleep to ensure template is in proper state before download + time.sleep(30) + + 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' not in template.status.split() or \ + 'Installing' not in template.status.split(): + raise Exception("ErrorInDownload") + + elif 'Downloaded' in template.status.split(): + time.sleep(10) + + elif timeout == 0: + break + + else: + time.sleep(10) + timeout = timeout - 1 + return + + @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): + """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 + # 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): + """Download an ISO""" + #Ensuring ISO is successfully downloaded + while True: + time.sleep(60) + + 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.split(): + 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, zoneid=None, domainid=None, + services=None, networkid=None): + """Associate Public IP address""" + cmd = associateIpAddress.associateIpAddressCmd() + cmd.account = accountid + cmd.zoneid = zoneid or services["zoneid"] + cmd.domainid = domainid or services["domainid"] + + if networkid: + cmd.networkid = networkid + + 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): + """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 + + 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): + """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 + + return NATRule(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): + """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 + + 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): + """Create Disk offering""" + cmd = createDiskOffering.createDiskOfferingCmd() + cmd.displaytext = services["displaytext"] + cmd.name = services["name"] + if custom: + cmd.customized = True + else: + cmd.disksize = services["disksize"] + 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 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): + """Create Load balancing Rule""" + + cmd = createLoadBalancerRule.createLoadBalancerRuleCmd() + cmd.publicipid = ipaddressid or services["ipaddressid"] + cmd.account = accountid or services["account"] + cmd.name = services["name"] + cmd.algorithm = services["alg"] + cmd.privateport = services["privateport"] + cmd.publicport = services["publicport"] + 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 + + @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 + + @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): + """Create Network for account""" + cmd = createNetwork.createNetworkCmd() + cmd.name = services["name"] + cmd.displaytext = services["displaytext"] + cmd.networkofferingid = services["networkoffering"] + cmd.zoneid = services["zoneid"] + if accountid: + cmd.account = accountid + if domainid: + cmd.domainid = domainid + + return Network(apiclient.createNetwork(cmd).__dict__) + + def delete(self, apiclient): + """Delete Account""" + + cmd = deleteNetwork.deleteNetworkCmd() + cmd.id = self.id + apiclient.deleteNetwork(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): + """Create VPN for Public IP address""" + cmd = createRemoteAccessVpn.createRemoteAccessVpnCmd() + cmd.publicipid = publicipid + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + + return Vpn(apiclient.createRemoteAccessVpn(cmd).__dict__) + + def delete(self, apiclient): + """Delete remote VPN access""" + + cmd = deleteRemoteAccessVpn.deleteRemoteAccessVpnCmd() + cmd.publicipid = self.publicipid + apiclient.deleteRemoteAccessVpn(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): + """Create VPN user""" + cmd = addVpnUser.addVpnUserCmd() + cmd.username = username + cmd.password = password + + if account: + cmd.account = account + if domainid: + cmd.domainid = domainid + + return VpnUser(apiclient.addVpnUser(cmd).__dict__) + + def delete(self, apiclient): + """Remove VPN user""" + + cmd = removeVpnUser.removeVpnUserCmd() + cmd.username = self.username + apiclient.removeVpnUser(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) + + @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 SecurityGroup: + """Manage Security Groups""" + + def __init__(self, items): + self.__dict__.update(items) + + @classmethod + def create(cls, apiclient, services, account=None, domainid=None, + description=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 + + 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): + """Authorize Ingress Rule""" + + cmd=authorizeSecurityGroupIngress.authorizeSecurityGroupIngressCmd() + + if domainid: + cmd.domainid = domainid + if account: + cmd.account = account + + 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) + + @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)) \ No newline at end of file diff --git a/tools/testClient/testcase/libs/common.py b/tools/testClient/testcase/libs/common.py new file mode 100644 index 00000000000..be0385e047a --- /dev/null +++ b/tools/testClient/testcase/libs/common.py @@ -0,0 +1,415 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# + +"""Common functions +""" + +#Import Local Modules +from cloudstackTestCase import * +from cloudstackAPI import * +import remoteSSHClient +from utils import * +from base import * + +#Import System modules +import time + +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): + """After setup wait for SSVMs to come Up""" + + time.sleep(30) + 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(30) + timeout = timeout - 1 + elif ssvm.state == 'Running': + break + elif timeout == 0: + raise Exception("SSVM failled to come up") + break + + timeout = 20 + 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(30) + timeout = timeout - 1 + elif cpvm.state == 'Running': + break + elif timeout == 0: + raise Exception("SSVM failled to come up") + break + return + +def download_builtin_templates(apiclient, zoneid, hypervisor, host, linklocalip): + """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" + ) + + # 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(30) + 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' not in template.status.split(): + raise Exception("ErrorInDownload") + elif 'Downloaded' in template.status.split(): + time.sleep(30) + return + +def update_resource_limit(apiclient, resourcetype, account=None, domainid=None, + max=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 + 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)) diff --git a/tools/testClient/testcase/libs/utils.py b/tools/testClient/testcase/libs/utils.py new file mode 100644 index 00000000000..9b6b05e785f --- /dev/null +++ b/tools/testClient/testcase/libs/utils.py @@ -0,0 +1,101 @@ +# -*- encoding: utf-8 -*- +# +# Copyright (c) 2012 Citrix. All rights reserved. +# + +"""Utilities functions +""" + +import time +import remoteSSHClient +from cloudstackAPI import * +import cloudstackConnection +#from cloudstackConnection import cloudConnection +import configGenerator +import logging +import string +import random + +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