From 49287568f3b2c605250a957c8ad519c5b00104ba Mon Sep 17 00:00:00 2001 From: Liping Xue Date: Mon, 5 Dec 2016 17:02:07 -0800 Subject: [PATCH 1/4] Add unit test to test AdminCLI command for "tenant". --- esx_service/cli/vmdkops_admin.py | 1 + esx_service/cli/vmdkops_admin_test.py | 195 ++++++++++++++++++++++++++ esx_service/utils/auth.py | 1 + esx_service/utils/auth_api.py | 7 +- esx_service/utils/auth_data.py | 5 +- esx_service/utils/auth_data_test.py | 10 ++ esx_service/utils/log_config.py | 2 +- esx_service/utils/vmdk_utils.py | 1 - esx_service/vmdk_ops_test.py | 17 ++- 9 files changed, 229 insertions(+), 10 deletions(-) diff --git a/esx_service/cli/vmdkops_admin.py b/esx_service/cli/vmdkops_admin.py index 809610cb7..8a4da9bc9 100644 --- a/esx_service/cli/vmdkops_admin.py +++ b/esx_service/cli/vmdkops_admin.py @@ -307,6 +307,7 @@ def commands(): 'help': "Datastore which access is controlled", 'required': True }, + '--default-datastore': { 'help': "Mark datastore as a default datastore for this tenant", 'action': 'store_true' diff --git a/esx_service/cli/vmdkops_admin_test.py b/esx_service/cli/vmdkops_admin_test.py index 3565047f9..4b44787ba 100644 --- a/esx_service/cli/vmdkops_admin_test.py +++ b/esx_service/cli/vmdkops_admin_test.py @@ -23,10 +23,35 @@ import volume_kv as kv import vmdkops_admin import random +import vmdk_ops_test +import auth_api # Number of expected columns in ADMIN_CLI ls EXPECTED_COLUMN_COUNT = 12 +# Number of expected columns in "tenant ls" +TENANT_LS_EXPECTED_COLUMN_COUNT = 5 + +def str_to_unicode(str): + """ convert str to unicode """ + return str.decode('utf-8') + +def generate_vm_name_str(vms): + """ Generate a str with concatenation of given list of vm name """ + # vms is a list of vm_name + # example: vms=["vm1", "vm2"] + # the return value is a string like this "vm1,vm2"" + res = "" + for vm in vms: + # vm[0] is vm_uuid, vm has format (vm_uuid) + res = res + vm + res = res + "," + + if res: + res = res[:-1] + + return res + class TestParsing(unittest.TestCase): """ Test command line arg parsing for all commands """ @@ -379,6 +404,176 @@ class TestStatus(unittest.TestCase): def test_status(self): self.assertEqual(vmdkops_admin.status(None), None) +class TestTenant(unittest.TestCase): + """ Test tenant functionality """ + # tenant1 info + tenant1_name = "test_tenant1" + vm1_name = 'test_vm1' + vm1 = None + vm2_name = 'test_vm2' + vm2 = None + tenant1_new_name = "new_test_tenant1" + datastore_name = None + datastore1_name = None + + def setUp(self): + """ Setup run before each test """ + + if (not self.datastore_name): + datastores = vmdk_utils.get_datastores() + if datastores: + datastore = datastores[0] + self.datastore_name = datastore[0] + self.datastore_path = datastore[2] + + if len(datastores) >= 1: + datastore1 = datastores[1] + self.datastore1_name = datastore1[0] + self.datastoer1_path = datastore[2] + + else: + + self.assertFalse(True) + + self.cleanup() + # get service_instance, and create VMs + si = vmdk_ops.get_si() + error, self.vm1 = vmdk_ops_test.create_vm(si=si, + vm_name=self.vm1_name, + datastore=self.datastore_name) + if error: + self.assertFalse(True) + + self.vm1_config_path = vmdk_utils.get_vm_config_path(self.vm1_name) + + error, self.vm2 = vmdk_ops_test.create_vm(si=si, + vm_name=self.vm2_name, + datastore=self.datastore_name) + if error: + self.assertFalse(True) + self.vm2_config_path = vmdk_utils.get_vm_config_path(self.vm2_name) + + def tearDown(self): + """ Cleanup after each test """ + self.cleanup() + + def cleanup(self): + # cleanup existing tenant + error_info = auth_api._tenant_rm( + name=self.tenant1_name, + remove_volumes=True) + + error_info = auth_api._tenant_rm( + name=self.tenant1_new_name, + remove_volumes=True) + + # remove VM + si = vmdk_ops.get_si() + vmdk_ops_test.remove_vm(si, self.vm1) + vmdk_ops_test.remove_vm(si, self.vm2) + + def test_tenant(self): + # create tenant1 without adding any vms and privileges + name = self.tenant1_name + vm_list = [self.vm1_name] + description = "Test tenant1" + privileges = [] + + error_info, tenant = auth_api._tenant_create( + name=name, + description=description, + vm_list=vm_list, + privileges=privileges) + self.assertEqual(None, error_info) + + error_info, tenant_list = auth_api._tenant_ls() + self.assertEqual(None, error_info) + + header = vmdkops_admin.tenant_ls_headers() + rows = vmdkops_admin.generate_tenant_ls_rows(tenant_list) + self.assertEqual(len(header), TENANT_LS_EXPECTED_COLUMN_COUNT) + + # Two tenants in the list, "_DEFAULT" and "test_tenant1" + # row[0] is for "_DEFAULT" tenant, and row[1] is for "test_tenant1" + self.assertEqual(len(rows), 2) + + # There are 5 columns for each row, the name of the columns are + # "Uuid", "Name", "Description", "Default_datastore", "VM_list" + # Sample output of row[1]: + # [u'9e1be0ce-3d58-40f6-a335-d6e267e34baa', u'test_tenant1', u'Test tenant1', '', 'test_vm1'] + expected_output = [str_to_unicode(self.tenant1_name), + str_to_unicode("Test tenant1"), + "", + generate_vm_name_str(vm_list)] + actual_output = [rows[1][1], + rows[1][2], + rows[1][3], + rows[1][4]] + + self.assertEqual(expected_output, actual_output) + + # tenant update to update description default_datastore + error_info = auth_api._tenant_update( + name=name, + description="This is test tenant1", + default_datastore=self.datastore_name) + self.assertEqual(None, error_info) + + error_info, tenant_list = auth_api._tenant_ls() + self.assertEqual(None, error_info) + + header = vmdkops_admin.tenant_ls_headers() + rows = vmdkops_admin.generate_tenant_ls_rows(tenant_list) + + expected_output = [str_to_unicode(self.tenant1_name), + str_to_unicode("This is test tenant1"), + self.datastore_name, + generate_vm_name_str(vm_list)] + actual_output = [rows[1][1], + rows[1][2], + rows[1][3], + rows[1][4]] + + self.assertEqual(expected_output, actual_output) + + # tenant update to rename the tenant + error_info = auth_api._tenant_update( + name=name, + new_name=self.tenant1_new_name) + self.assertEqual(None, error_info) + + error_info, tenant_list = auth_api._tenant_ls() + self.assertEqual(None, error_info) + + header = vmdkops_admin.tenant_ls_headers() + rows = vmdkops_admin.generate_tenant_ls_rows(tenant_list) + + expected_output = [str_to_unicode(self.tenant1_new_name), + str_to_unicode("This is test tenant1"), + self.datastore_name, + generate_vm_name_str(vm_list)] + actual_output = [rows[1][1], + rows[1][2], + rows[1][3], + rows[1][4]] + + + # tenant rm to remove the tenant + error_info = auth_api._tenant_rm( + name=self.tenant1_new_name, + remove_volumes=True) + self.assertEqual(None, error_info) + + error_info, tenant_list = auth_api._tenant_ls() + self.assertEqual(None, error_info) + + header = vmdkops_admin.tenant_ls_headers() + rows = vmdkops_admin.generate_tenant_ls_rows(tenant_list) + + # right now, should only have 1 tenant, which is "_DEFAULT" tenant + self.assertEqual(len(rows), 1) + + if __name__ == '__main__': kv.init() unittest.main() diff --git a/esx_service/utils/auth.py b/esx_service/utils/auth.py index cff1bf9cb..df2ce183e 100644 --- a/esx_service/utils/auth.py +++ b/esx_service/utils/auth.py @@ -210,6 +210,7 @@ def has_privilege(privileges, type=None): # privileges is None, return False if not privileges: return False + if type: logging.debug("has_privilege: %s=%d", type, privileges[type]) return privileges[type] diff --git a/esx_service/utils/auth_api.py b/esx_service/utils/auth_api.py index e565e7d00..ef2538ac6 100644 --- a/esx_service/utils/auth_api.py +++ b/esx_service/utils/auth_api.py @@ -214,7 +214,6 @@ def get_default_datastore(name): error_info, default_datastore = tenant.get_default_datastore(auth_mgr.conn) return error_info, default_datastore - def _tenant_create(name, description="", vm_list=None, privileges=None): """ API to create a tenant """ logging.debug("_tenant_create: name=%s description=%s vm_list=%s privileges=%s", name, description, vm_list, privileges) @@ -377,7 +376,7 @@ def _tenant_access_add(name, datastore, allow_create=None, default_datastore=Fal error_info, auth_mgr = get_auth_mgr() if error_info: return error_info - + error_info = tenant.set_datastore_access_privileges(auth_mgr.conn, [privileges]) if error_info: return error_info @@ -389,6 +388,7 @@ def _tenant_access_add(name, datastore, allow_create=None, default_datastore=Fal return error_info logging.debug("_tenant_access_add: get_row_from_privileges_table for tenant id=%s return %s", tenant.id, result) + if len(result) == 1 or default_datastore: if datastore == auth.DEFAULT_DS: datastore_url = auth.DEFAULT_DS_URL @@ -447,6 +447,7 @@ def _tenant_access_rm(name, datastore): error_info, auth_mgr = get_auth_mgr() if error_info: return error_info + if datastore == auth.DEFAULT_DS: datastore_url = auth.DEFAULT_DS_URL else: @@ -464,7 +465,7 @@ def _tenant_access_rm(name, datastore): if default_datastore == datastore: error_info = tenant.set_default_datastore(auth_mgr.conn, "") - + return error_info def _tenant_access_ls(name): diff --git a/esx_service/utils/auth_data.py b/esx_service/utils/auth_data.py index 4da958009..9791ec96d 100644 --- a/esx_service/utils/auth_data.py +++ b/esx_service/utils/auth_data.py @@ -203,7 +203,6 @@ def get_default_datastore(self, conn): error_info: return None on success or error info on failure datastore: return default_datastore name on success or None on failure """ - error_info, result = auth.get_row_from_tenants_table(conn, self.id) if error_info: logging.error("Error %s when getting default datastore for tenant_id %s", @@ -218,7 +217,6 @@ def get_default_datastore(self, conn): else: datastore = vmdk_utils.get_datastore_name(datastore_url) return None, datastore - def set_datastore_access_privileges(self, conn, privileges): """ Set datastore and privileges for this tenant. @@ -359,7 +357,6 @@ def create_default_privileges(self): this privilege will have full permission (create, delete, and mount) and no max_volume_size and usage_quota limitation """ - privileges = [{'datastore_url': auth.DEFAULT_DS_URL, 'allow_create': 1, 'max_volume_size': 0, @@ -379,7 +376,6 @@ def create_default_privileges(self): def get_db_version(self): """ Get DB schema version from auth-db - """ major_ver = 0 minor_ver = 0 @@ -609,6 +605,7 @@ def create_tenant(self, name, description, vms, privileges): vms=vms, privileges=privileges, default_datastore_url=default_datastore_url) + id = tenant.id try: self.conn.execute( diff --git a/esx_service/utils/auth_data_test.py b/esx_service/utils/auth_data_test.py index 83d119a87..8bb9ffa2b 100644 --- a/esx_service/utils/auth_data_test.py +++ b/esx_service/utils/auth_data_test.py @@ -177,6 +177,7 @@ def test_remove_vms(self): description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant1.id)) error_info = tenant1.remove_vms(self.auth_mgr.conn, vms) @@ -197,6 +198,7 @@ def test_set_name(self): description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant1.id)) @@ -217,6 +219,7 @@ def test_set_description(self): description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant1.id)) error_info = tenant1.set_description(self.auth_mgr.conn, 'new description') @@ -237,6 +240,7 @@ def test_set_default_datastore(self): description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant1.id)) @@ -260,6 +264,7 @@ def test_add_datastore_access_privileges(self): description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant1.id)) @@ -309,6 +314,7 @@ def test_list_tenants(self): description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant1.id)) @@ -320,6 +326,7 @@ def test_list_tenants(self): description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant2.id)) @@ -422,10 +429,12 @@ def test_remove_tenants(self): privileges = self.get_privileges() default_datastore = self.get_default_datastore() default_datastore_url = self.get_datastore_url(default_datastore) + error_info, tenant1 = self.auth_mgr.create_tenant(name='tenant1', description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant1.id)) @@ -435,6 +444,7 @@ def test_remove_tenants(self): description='Some tenant', vms=vms, privileges=privileges) + self.assertEqual(error_info, None) self.assertTrue(uuid.UUID(tenant2.id)) diff --git a/esx_service/utils/log_config.py b/esx_service/utils/log_config.py index 517eb4730..dbd9150f8 100644 --- a/esx_service/utils/log_config.py +++ b/esx_service/utils/log_config.py @@ -30,7 +30,7 @@ # since we rely on it to locate log file name after config is loaded. LOG_CONFIG_FILE = "/etc/vmware/vmdkops/log_config.json" -LOG_LEVEL_DEFAULT = 'INFO' +LOG_LEVEL_DEFAULT = 'DEBUG' # Defaults for log files - used to generate conf file if it is missing # Note: log file location should be synced with CI and 'make' diff --git a/esx_service/utils/vmdk_utils.py b/esx_service/utils/vmdk_utils.py index bf30f8c6c..61e1249c2 100644 --- a/esx_service/utils/vmdk_utils.py +++ b/esx_service/utils/vmdk_utils.py @@ -318,7 +318,6 @@ def get_datastore_url(datastore_name): def get_datastore_name(datastore_url): """ return datastore name for given datastore url """ res = [d.info.name for d in get_datastore_objects() if d.info.url == datastore_url] - return res[0] def main(): log_config.configure() diff --git a/esx_service/vmdk_ops_test.py b/esx_service/vmdk_ops_test.py index 8629f804d..39a3c12f5 100644 --- a/esx_service/vmdk_ops_test.py +++ b/esx_service/vmdk_ops_test.py @@ -612,7 +612,7 @@ def test_vmdkop_authorize(self): self.assertEqual(error_info, "No create privilege" ) # set "create_volume" privilege to true - privileges = [{'datastore_url': datastore_url, + privileges = [{'datastore_url': datastore_url 'allow_create': 1, 'max_volume_size': 500, 'usage_quota': 1000}] @@ -896,6 +896,7 @@ def test_vmdkops_on_tenant_vm(self): # test attach a volume opts={} result = vmdk_ops.executeRequest(vm1_uuid, self.vm1_name, self.vm1_config_path, auth.CMD_ATTACH, self.tenant1_vol1_name, opts) + print result self.assertFalse("Error" in result) # test detach a volume @@ -914,6 +915,20 @@ def test_vmdkops_on_tenant_vm(self): default_datastore=False, volume_maxsize=volume_maxsize, volume_totalsize=volume_totalsize) + + # run create command again, which should succeed since "default_datastore" is set for tenant1 now + opts={u'fstype': u'ext4'} + error_info = vmdk_ops.executeRequest(vm1_uuid, self.vm1_name, self.vm1_config_path, auth.CMD_CREATE, self.tenant1_vol1_name, opts) + self.assertEqual(None, error_info) + + # test attach a volume + opts={} + result = vmdk_ops.executeRequest(vm1_uuid, self.vm1_name, self.vm1_config_path, auth.CMD_ATTACH, self.tenant1_vol1_name, opts) + self.assertFalse("Error" in result) + + # test detach a volume + error_info = vmdk_ops.executeRequest(vm1_uuid, self.vm1_name, self.vm1_config_path, auth.CMD_DETACH, self.tenant1_vol1_name, opts) + self.assertEqual(None, error_info) # create a volume with 600MB which exceed the volume_maxsize opts={u'size': u'600MB', u'fstype': u'ext4'} From 2a6b688d21ea622dccba3ddd042d2349ebbf20f1 Mon Sep 17 00:00:00 2001 From: Liping Xue Date: Mon, 19 Dec 2016 17:00:03 -0800 Subject: [PATCH 2/4] Add unit test for AdminCLI command for "tenant vm" and "tenant access". --- esx_service/cli/vmdkops_admin.py | 1 - esx_service/cli/vmdkops_admin_test.py | 293 ++++++++++++++++++++++---- esx_service/utils/auth.py | 1 - esx_service/utils/log_config.py | 2 +- esx_service/utils/vmdk_utils.py | 1 + esx_service/vmdk_ops_test.py | 34 ++- 6 files changed, 271 insertions(+), 61 deletions(-) diff --git a/esx_service/cli/vmdkops_admin.py b/esx_service/cli/vmdkops_admin.py index 8a4da9bc9..809610cb7 100644 --- a/esx_service/cli/vmdkops_admin.py +++ b/esx_service/cli/vmdkops_admin.py @@ -307,7 +307,6 @@ def commands(): 'help': "Datastore which access is controlled", 'required': True }, - '--default-datastore': { 'help': "Mark datastore as a default datastore for this tenant", 'action': 'store_true' diff --git a/esx_service/cli/vmdkops_admin_test.py b/esx_service/cli/vmdkops_admin_test.py index 4b44787ba..1aedfd0e9 100644 --- a/esx_service/cli/vmdkops_admin_test.py +++ b/esx_service/cli/vmdkops_admin_test.py @@ -25,6 +25,8 @@ import random import vmdk_ops_test import auth_api +import log_config +import logging # Number of expected columns in ADMIN_CLI ls EXPECTED_COLUMN_COUNT = 12 @@ -32,9 +34,19 @@ # Number of expected columns in "tenant ls" TENANT_LS_EXPECTED_COLUMN_COUNT = 5 -def str_to_unicode(str): - """ convert str to unicode """ - return str.decode('utf-8') +# Number of expected columns in "tenant vm ls" +TENANT_VM_LS_EXPECTED_COLUMN_COUNT = 2 + +# Number of expected columns in "tenant vm ls" +TENANT_ACCESS_LS_EXPECTED_COLUMN_COUNT = 4 + +def convert_to_str(unicode_or_str): + python_version = sys.version_info.major + if python_version >= 3: + return unicode_or_str + else: + # convert the input from unicode to str + return unicode_or_str.encode('utf-8') def generate_vm_name_str(vms): """ Generate a str with concatenation of given list of vm name """ @@ -408,9 +420,11 @@ class TestTenant(unittest.TestCase): """ Test tenant functionality """ # tenant1 info tenant1_name = "test_tenant1" - vm1_name = 'test_vm1' + random_id = random.randint(0, 65536) + vm1_name = 'test_vm1_'+str(random_id) vm1 = None - vm2_name = 'test_vm2' + random_id = random.randint(0, 65536) + vm2_name = 'test_vm2_'+str(random_id) vm2 = None tenant1_new_name = "new_test_tenant1" datastore_name = None @@ -440,18 +454,23 @@ def setUp(self): si = vmdk_ops.get_si() error, self.vm1 = vmdk_ops_test.create_vm(si=si, vm_name=self.vm1_name, - datastore=self.datastore_name) + datastore_name=self.datastore_name) if error: self.assertFalse(True) self.vm1_config_path = vmdk_utils.get_vm_config_path(self.vm1_name) + logging.info("TestTenant: create vm1 name=%s Done", self.vm1_name) + error, self.vm2 = vmdk_ops_test.create_vm(si=si, vm_name=self.vm2_name, - datastore=self.datastore_name) + datastore_name=self.datastore_name) if error: self.assertFalse(True) self.vm2_config_path = vmdk_utils.get_vm_config_path(self.vm2_name) + + logging.info("TestTenant: create vm2 name=%s Done", self.vm2_name) + def tearDown(self): """ Cleanup after each test """ @@ -473,17 +492,14 @@ def cleanup(self): vmdk_ops_test.remove_vm(si, self.vm2) def test_tenant(self): - # create tenant1 without adding any vms and privileges - name = self.tenant1_name + """ Test AdminCLI command for tenant management """ + # create tenant1 vm_list = [self.vm1_name] - description = "Test tenant1" - privileges = [] - error_info, tenant = auth_api._tenant_create( - name=name, - description=description, + name=self.tenant1_name, + description="Test tenant1" , vm_list=vm_list, - privileges=privileges) + privileges=[]) self.assertEqual(None, error_info) error_info, tenant_list = auth_api._tenant_ls() @@ -494,27 +510,28 @@ def test_tenant(self): self.assertEqual(len(header), TENANT_LS_EXPECTED_COLUMN_COUNT) # Two tenants in the list, "_DEFAULT" and "test_tenant1" - # row[0] is for "_DEFAULT" tenant, and row[1] is for "test_tenant1" + # rows[0] is for "_DEFAULT" tenant, and rows[1] is for "test_tenant1" self.assertEqual(len(rows), 2) # There are 5 columns for each row, the name of the columns are # "Uuid", "Name", "Description", "Default_datastore", "VM_list" - # Sample output of row[1]: + # Sample output of rows[1]: # [u'9e1be0ce-3d58-40f6-a335-d6e267e34baa', u'test_tenant1', u'Test tenant1', '', 'test_vm1'] - expected_output = [str_to_unicode(self.tenant1_name), - str_to_unicode("Test tenant1"), + expected_output = [self.tenant1_name, + "Test tenant1", "", generate_vm_name_str(vm_list)] - actual_output = [rows[1][1], - rows[1][2], - rows[1][3], - rows[1][4]] + actual_output = [convert_to_str(rows[1][1]), + convert_to_str(rows[1][2]), + convert_to_str(rows[1][3]), + convert_to_str(rows[1][4]) + ] self.assertEqual(expected_output, actual_output) - # tenant update to update description default_datastore + # tenant update to update description and default_datastore error_info = auth_api._tenant_update( - name=name, + name=self.tenant1_name, description="This is test tenant1", default_datastore=self.datastore_name) self.assertEqual(None, error_info) @@ -525,20 +542,21 @@ def test_tenant(self): header = vmdkops_admin.tenant_ls_headers() rows = vmdkops_admin.generate_tenant_ls_rows(tenant_list) - expected_output = [str_to_unicode(self.tenant1_name), - str_to_unicode("This is test tenant1"), + expected_output = [self.tenant1_name, + "This is test tenant1", self.datastore_name, generate_vm_name_str(vm_list)] - actual_output = [rows[1][1], - rows[1][2], - rows[1][3], - rows[1][4]] + actual_output = [convert_to_str(rows[1][1]), + convert_to_str(rows[1][2]), + convert_to_str(rows[1][3]), + convert_to_str(rows[1][4]) + ] self.assertEqual(expected_output, actual_output) # tenant update to rename the tenant error_info = auth_api._tenant_update( - name=name, + name=self.tenant1_name, new_name=self.tenant1_new_name) self.assertEqual(None, error_info) @@ -548,14 +566,15 @@ def test_tenant(self): header = vmdkops_admin.tenant_ls_headers() rows = vmdkops_admin.generate_tenant_ls_rows(tenant_list) - expected_output = [str_to_unicode(self.tenant1_new_name), - str_to_unicode("This is test tenant1"), + expected_output = [self.tenant1_new_name, + "This is test tenant1", self.datastore_name, generate_vm_name_str(vm_list)] - actual_output = [rows[1][1], - rows[1][2], - rows[1][3], - rows[1][4]] + actual_output = [convert_to_str(rows[1][1]), + convert_to_str(rows[1][2]), + convert_to_str(rows[1][3]), + convert_to_str(rows[1][4]) + ] # tenant rm to remove the tenant @@ -572,8 +591,206 @@ def test_tenant(self): # right now, should only have 1 tenant, which is "_DEFAULT" tenant self.assertEqual(len(rows), 1) + + def test_tenant_vm(self): + """ Test AdminCLI command for tenant vm management """ + # create tenant1 without adding any vms and privilege + error_info, tenant = auth_api._tenant_create( + name=self.tenant1_name, + description="Test tenant1", + vm_list=[], + privileges=[]) + self.assertEqual(None, error_info) + + error_info, vms = auth_api._tenant_vm_ls(self.tenant1_name) + self.assertEqual(None, error_info) + + headers = vmdkops_admin.tenant_vm_ls_headers() + rows = vmdkops_admin.generate_tenant_vm_ls_rows(vms) + + self.assertEqual(len(headers), TENANT_VM_LS_EXPECTED_COLUMN_COUNT) + expected_output = [] + actual_output = rows + self.assertEqual(expected_output, actual_output) + + # tenant vm add to add two VMs to the tenant + error_info = auth_api._tenant_vm_add( + name=self.tenant1_name, + vm_list=[self.vm1_name, self.vm2_name]) + self.assertEqual(None, error_info) + + error_info, vms = auth_api._tenant_vm_ls(self.tenant1_name) + self.assertEqual(None, error_info) + + # There are 2 columns for each row, the name of the columns are + # "Uuid", "Name" + # Sample output of a row: + # [u'564d2b7d-187c-eaaf-60bc-e015b5cdc3eb', 'test_vm1'] + rows = vmdkops_admin.generate_tenant_vm_ls_rows(vms) + # Two vms are associated with this tenant + self.assertEqual(len(rows), 2) + + expected_output = [self.vm1_name, self.vm2_name] + actual_output = [rows[0][1], + rows[1][1]] + self.assertEqual(expected_output, actual_output) + + # tenant vm rm to remove one VM from the tenant + error_info = auth_api._tenant_vm_rm( + name=self.tenant1_name, + vm_list=[self.vm2_name]) + self.assertEqual(None, error_info) + + error_info, vms = auth_api._tenant_vm_ls(self.tenant1_name) + self.assertEqual(None, error_info) + + rows = vmdkops_admin.generate_tenant_vm_ls_rows(vms) + + # tenant should only have one VM now + self.assertEqual(len(rows), 1) + + expected_output = [self.vm1_name] + actual_output = [rows[0][1]] + self.assertEqual(expected_output, actual_output) + + def test_tenant_access(self): + """ Test AdminCLI command for tenant access management """ + + # create tenant1 without adding any vms and privileges + error_info, tenant = auth_api._tenant_create( + name=self.tenant1_name, + description="Test tenant1", + vm_list=[self.vm1_name], + privileges=[]) + self.assertEqual(None, error_info) + + # add first access privilege for tenant + # allow_create = False + # max_volume size = 600MB + # total_volume size = 1GB + error_info = auth_api._tenant_access_add(name=self.tenant1_name, + datastore=self.datastore_name, + default_datastore=False, + allow_create=False, + volume_maxsize="600MB", + volume_totalsize="1GB" + ) + self.assertEqual(None, error_info) + + error_info, privileges = auth_api._tenant_access_ls(self.tenant1_name) + self.assertEqual(None, error_info) + + header = vmdkops_admin.tenant_access_ls_headers() + # There are 4 columns for each row, the name of the columns are + # "Datastore", "Allow_create", "Max_volume_size", "Total_size" + # Sample output of a row: + # ['datastore1', 'False', '600.00MB', '1.00GB'] + rows = vmdkops_admin.generate_tenant_access_ls_rows(privileges) + self.assertEqual(len(header), TENANT_ACCESS_LS_EXPECTED_COLUMN_COUNT) + + # tenant aceess privilege should only have a row now + self.assertEqual(len(rows), 1) + + expected_output = [self.datastore_name, + "False", + "600.00MB", + "1.00GB"] + actual_output = [rows[0][0], + rows[0][1], + rows[0][2], + rows[0][3]] + self.assertEqual(expected_output, actual_output) + + # update the access privileges + # change allow_create to True + # change max_volume size to 1000MB + # change total_volume size to 2GB + error_info = auth_api._tenant_access_set(name=self.tenant1_name, + datastore=self.datastore_name, + allow_create=True, + volume_maxsize="1000MB", + volume_totalsize="3GB" + ) + self.assertEqual(None, error_info) + + error_info, privileges = auth_api._tenant_access_ls(self.tenant1_name) + self.assertEqual(None, error_info) + + rows = vmdkops_admin.generate_tenant_access_ls_rows(privileges) + self.assertEqual(len(rows), 1) + + + expected_output = [self.datastore_name, + "True", + "1000.00MB", + "3.00GB"] + actual_output = [rows[0][0], + rows[0][1], + rows[0][2], + rows[0][3]] + self.assertEqual(expected_output, actual_output) + + if self.datastore1_name: + # second datastore is available, can test tenant access add with --default_datastore + error_info, tenant_list = auth_api._tenant_ls() + self.assertEqual(None, error_info) + + # Two tenants in the list, "_DEFAULT" and "test_tenant1" + # rows[0] is for "_DEFAULT" tenant, and rows[1] is for "test_tenant1" + + # There are 5 columns for each row, the name of the columns are + # "Uuid", "Name", "Description", "Default_datastore", "VM_list" + # Sample output of one row: + # [u'9e1be0ce-3d58-40f6-a335-d6e267e34baa', u'test_tenant1', u'Test tenant1', '', 'test_vm1'] + rows = vmdkops_admin.generate_tenant_ls_rows(tenant_list) + + # get "default_datastore" from the output + actual_output = rows[1][3] + expected_output = self.datastore_name + self.assertEqual(expected_output, actual_output) + + # add second access privilege for tenant + # allow_create = False + # max_volume size = 600MB + # total_volume size = 1GB + error_info = auth_api._tenant_access_add(name=self.tenant1_name, + datastore=self.datastore1_name, + default_datastore=True, + allow_create=False, + volume_maxsize="600MB", + volume_totalsize="1GB" + ) + self.assertEqual(None, error_info) + + error_info, tenant_list = auth_api._tenant_ls() + self.assertEqual(None, error_info) + + + rows = vmdkops_admin.generate_tenant_ls_rows(tenant_list) + # get "default_datastore" from the output + actual_output = rows[1][3] + expected_output = self.datastore1_name + self.assertEqual(expected_output, actual_output) + + error_info = auth_api._tenant_access_rm(name=self.tenant1_name, + datastore=self.datastore1_name) + self.assertEqual(error_info, None) + + # remove access privileges + error_info = auth_api._tenant_access_rm(name=self.tenant1_name, + datastore=self.datastore_name) + + self.assertEqual(None, error_info) + + error_info, privileges = auth_api._tenant_access_ls(self.tenant1_name) + self.assertEqual(None, error_info) + + rows = vmdkops_admin.generate_tenant_access_ls_rows(privileges) + # no tenant access privilege available for this tenant + self.assertEqual(rows, []) if __name__ == '__main__': kv.init() + log_config.configure() unittest.main() diff --git a/esx_service/utils/auth.py b/esx_service/utils/auth.py index df2ce183e..cff1bf9cb 100644 --- a/esx_service/utils/auth.py +++ b/esx_service/utils/auth.py @@ -210,7 +210,6 @@ def has_privilege(privileges, type=None): # privileges is None, return False if not privileges: return False - if type: logging.debug("has_privilege: %s=%d", type, privileges[type]) return privileges[type] diff --git a/esx_service/utils/log_config.py b/esx_service/utils/log_config.py index dbd9150f8..517eb4730 100644 --- a/esx_service/utils/log_config.py +++ b/esx_service/utils/log_config.py @@ -30,7 +30,7 @@ # since we rely on it to locate log file name after config is loaded. LOG_CONFIG_FILE = "/etc/vmware/vmdkops/log_config.json" -LOG_LEVEL_DEFAULT = 'DEBUG' +LOG_LEVEL_DEFAULT = 'INFO' # Defaults for log files - used to generate conf file if it is missing # Note: log file location should be synced with CI and 'make' diff --git a/esx_service/utils/vmdk_utils.py b/esx_service/utils/vmdk_utils.py index 61e1249c2..bf30f8c6c 100644 --- a/esx_service/utils/vmdk_utils.py +++ b/esx_service/utils/vmdk_utils.py @@ -318,6 +318,7 @@ def get_datastore_url(datastore_name): def get_datastore_name(datastore_url): """ return datastore name for given datastore url """ res = [d.info.name for d in get_datastore_objects() if d.info.url == datastore_url] + return res[0] def main(): log_config.configure() diff --git a/esx_service/vmdk_ops_test.py b/esx_service/vmdk_ops_test.py index 39a3c12f5..889a2d70c 100644 --- a/esx_service/vmdk_ops_test.py +++ b/esx_service/vmdk_ops_test.py @@ -38,6 +38,7 @@ import auth_api import error_code import vmdk_utils +import random # Max volumes count we can attach to a singe VM. MAX_VOL_COUNT_FOR_ATTACH = 60 @@ -415,7 +416,8 @@ def create_vm(si, vm_name, datastore_name): task = vm_folder.CreateVM_Task(config=config, pool=resource_pool) vmdk_ops.wait_for_tasks(si, [task]) - + + logging.info("create_vm: vm_name=%s, datastore_name=%s", vm_name, datastore_name) vm = task.info.result if vm: logging.debug("Found: VM %s", vm_name) @@ -447,7 +449,8 @@ class VmdkAttachDetachTestCase(unittest.TestCase): """ Unit test for VMDK Attach and Detach ops """ volNamePre = "vol_UnitTest_Attach" - vm_name = 'test-vm' + random_id = random.randint(0, 65536) + vm_name = 'test-vm_'+str(random_id) vm = None if config["run_max_attach"]: max_vol_count = MAX_VOL_COUNT_FOR_ATTACH @@ -612,7 +615,7 @@ def test_vmdkop_authorize(self): self.assertEqual(error_info, "No create privilege" ) # set "create_volume" privilege to true - privileges = [{'datastore_url': datastore_url + privileges = [{'datastore_url': datastore_url, 'allow_create': 1, 'max_volume_size': 500, 'usage_quota': 1000}] @@ -661,7 +664,8 @@ class VmdkTenantTestCase(unittest.TestCase): # tenant1 info tenant1_name = "test_tenant1" - vm1_name = 'test_vm1' + random_id = random.randint(0, 65536) + vm1_name = 'test_vm1_'+str(random_id) vm1 = None tenant1_vol1_name = 'tenant1_vol1' tenant1_vol2_name = 'tenant1_vol2' @@ -671,7 +675,8 @@ class VmdkTenantTestCase(unittest.TestCase): # tenant2 info tenant2_name = "test_tenant2" - vm2_name = 'test_vm2' + random_id = random.randint(0, 65536) + vm2_name = 'test_vm2_'+str(random_id) vm2 = None tenant2_vol1_name = 'tenant2_vol1' tenant2_vol2_name = 'tenant2_vol2' @@ -746,6 +751,7 @@ def setUp(self): self.assertFalse(True) self.vm1_config_path = vmdk_utils.get_vm_config_path(self.vm1_name) + logging.info("VmdkTenantTestCase: create vm1 name=%s Done", self.vm1_name) error, self.vm2 = create_vm(si=si, vm_name=self.vm2_name, @@ -753,6 +759,7 @@ def setUp(self): if error: self.assertFalse(True) self.vm2_config_path = vmdk_utils.get_vm_config_path(self.vm2_name) + logging.info("VmdkTenantTestCase: create vm2 name=%s Done", self.vm2_name) # create DEFAULT tenant DEFAULT privilege if missing self.create_default_tenant_and_privileges() @@ -817,7 +824,7 @@ def cleanup(self): si = vmdk_ops.get_si() remove_vm(si, self.vm1) remove_vm(si, self.vm2) - + def test_vmdkops_on_default_tenant_vm(self): """ Test vmdk life cycle on a VM which belongs to DEFAULT tenant """ # This test test the following cases: @@ -896,7 +903,6 @@ def test_vmdkops_on_tenant_vm(self): # test attach a volume opts={} result = vmdk_ops.executeRequest(vm1_uuid, self.vm1_name, self.vm1_config_path, auth.CMD_ATTACH, self.tenant1_vol1_name, opts) - print result self.assertFalse("Error" in result) # test detach a volume @@ -915,19 +921,6 @@ def test_vmdkops_on_tenant_vm(self): default_datastore=False, volume_maxsize=volume_maxsize, volume_totalsize=volume_totalsize) - - # run create command again, which should succeed since "default_datastore" is set for tenant1 now - opts={u'fstype': u'ext4'} - error_info = vmdk_ops.executeRequest(vm1_uuid, self.vm1_name, self.vm1_config_path, auth.CMD_CREATE, self.tenant1_vol1_name, opts) - self.assertEqual(None, error_info) - - # test attach a volume - opts={} - result = vmdk_ops.executeRequest(vm1_uuid, self.vm1_name, self.vm1_config_path, auth.CMD_ATTACH, self.tenant1_vol1_name, opts) - self.assertFalse("Error" in result) - - # test detach a volume - error_info = vmdk_ops.executeRequest(vm1_uuid, self.vm1_name, self.vm1_config_path, auth.CMD_DETACH, self.tenant1_vol1_name, opts) self.assertEqual(None, error_info) # create a volume with 600MB which exceed the volume_maxsize @@ -1112,6 +1105,7 @@ def test_vmdkops_on_different_tenants(self): @unittest.skipIf(len(vmdk_utils.get_datastores()) < 2, "second datastore is not found - skipping this test") + @unittest.skip("Liping remove") def test_vmdkops_for_default_datastore(self): """ Test vmdkops for default_datastore """ logging.info("test_vmdkops_for_default_datastore") From 23d249d4c1360266c796f36ac0b15e62db929452 Mon Sep 17 00:00:00 2001 From: Liping Xue Date: Tue, 10 Jan 2017 10:50:17 -0800 Subject: [PATCH 3/4] Address comments from Mark to add description of which are convered in the test. --- esx_service/cli/vmdkops_admin_test.py | 18 +++++++++++++++++- esx_service/vmdk_ops_test.py | 1 - 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/esx_service/cli/vmdkops_admin_test.py b/esx_service/cli/vmdkops_admin_test.py index 1aedfd0e9..3930a5e5e 100644 --- a/esx_service/cli/vmdkops_admin_test.py +++ b/esx_service/cli/vmdkops_admin_test.py @@ -417,7 +417,23 @@ def test_status(self): self.assertEqual(vmdkops_admin.status(None), None) class TestTenant(unittest.TestCase): - """ Test tenant functionality """ + """ + Test tenant functionality + """ + + # The following tests are covered: + # 1. tenant command + # Test tenant create, tenant ls and tenant rm + # tenant update to update tenant description and default_datastore + # tenant update to rename a tenant + # 2. tenant vm command + # tenant vm add , tenant vm rm and tenant vm ls + # 3. tenant access command + # tenant access create, tenant access ls and tenant access rm + # tenant access set command to update allow_create, volume_maxsize and volume_totalsize + # tenant access set command to update default_datastore + # Test convered are mainly positive test, no negative tests are done here + # tenant1 info tenant1_name = "test_tenant1" random_id = random.randint(0, 65536) diff --git a/esx_service/vmdk_ops_test.py b/esx_service/vmdk_ops_test.py index 889a2d70c..2c4a84c71 100644 --- a/esx_service/vmdk_ops_test.py +++ b/esx_service/vmdk_ops_test.py @@ -1105,7 +1105,6 @@ def test_vmdkops_on_different_tenants(self): @unittest.skipIf(len(vmdk_utils.get_datastores()) < 2, "second datastore is not found - skipping this test") - @unittest.skip("Liping remove") def test_vmdkops_for_default_datastore(self): """ Test vmdkops for default_datastore """ logging.info("test_vmdkops_for_default_datastore") From 287fce28c4c8a35b5c0511fea9715137a0c9df03 Mon Sep 17 00:00:00 2001 From: Liping Xue Date: Tue, 10 Jan 2017 11:17:59 -0800 Subject: [PATCH 4/4] Address minor comment from Nirdesh. --- esx_service/cli/vmdkops_admin_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esx_service/cli/vmdkops_admin_test.py b/esx_service/cli/vmdkops_admin_test.py index 3930a5e5e..06987c0f0 100644 --- a/esx_service/cli/vmdkops_admin_test.py +++ b/esx_service/cli/vmdkops_admin_test.py @@ -456,7 +456,7 @@ def setUp(self): self.datastore_name = datastore[0] self.datastore_path = datastore[2] - if len(datastores) >= 1: + if len(datastores) > 1: datastore1 = datastores[1] self.datastore1_name = datastore1[0] self.datastoer1_path = datastore[2]