diff --git a/delfin/drivers/dell_emc/unity/rest_handler.py b/delfin/drivers/dell_emc/unity/rest_handler.py index c9e28239a..d482e2fa0 100644 --- a/delfin/drivers/dell_emc/unity/rest_handler.py +++ b/delfin/drivers/dell_emc/unity/rest_handler.py @@ -281,6 +281,30 @@ def get_quota_configs(self): result_json = self.get_rest_info(url) return result_json + def get_host_initiators(self, page): + url = '/api/types/hostInitiator/instances?%s&page=%s' % \ + ('fields=id,health,type,parentHost,initiatorId', page) + result_json = self.get_rest_info(url) + return result_json + + def get_all_hosts(self, page): + url = '/api/types/host/instances?%s&page=%s' \ + % ('fields=id,health,name,description,osType', page) + result_json = self.get_rest_info(url) + return result_json + + def get_host_ip(self): + url = '/api/types/hostIPPort/instances?%s' % \ + ('fields=id,name,address,netmask,host') + result_json = self.get_rest_info(url) + return result_json + + def get_host_lun(self, page): + url = '/api/types/hostLUN/instances?%s&page=%s' % \ + ('fields=id,host,lun', page) + result_json = self.get_rest_info(url) + return result_json + def get_history_metrics(self, path, page): url = '/api/types/metricValue/instances?filter=path EQ "%s"&page=%s'\ % (path, page) diff --git a/delfin/drivers/dell_emc/unity/unity.py b/delfin/drivers/dell_emc/unity/unity.py index 1edb159d6..8dfce7175 100644 --- a/delfin/drivers/dell_emc/unity/unity.py +++ b/delfin/drivers/dell_emc/unity/unity.py @@ -107,6 +107,34 @@ class UnityStorDriver(driver.StorageDriver): } MS_PER_HOUR = 60 * 60 * 1000 + OS_TYPE_MAP = {'AIX': constants.HostOSTypes.AIX, + 'Citrix XenServer': constants.HostOSTypes.XEN_SERVER, + 'HP-UX': constants.HostOSTypes.HP_UX, + 'IBM VIOS': constants.HostOSTypes.UNKNOWN, + 'Linux': constants.HostOSTypes.LINUX, + 'Mac OS': constants.HostOSTypes.MAC_OS, + 'Solaris': constants.HostOSTypes.SOLARIS, + 'VMware ESXi': constants.HostOSTypes.VMWARE_ESX, + 'Windows Client': constants.HostOSTypes.WINDOWS, + 'Windows Server': constants.HostOSTypes.WINDOWS + } + INITIATOR_STATUS_MAP = {5: constants.InitiatorStatus.ONLINE, + 7: constants.InitiatorStatus.ONLINE, + 15: constants.InitiatorStatus.ONLINE, + 20: constants.InitiatorStatus.ONLINE, + 10: constants.InitiatorStatus.OFFLINE + } + HOST_STATUS_MAP = {5: constants.HostStatus.NORMAL, + 7: constants.HostStatus.NORMAL, + 15: constants.HostStatus.NORMAL, + 20: constants.HostStatus.NORMAL, + 10: constants.HostStatus.DEGRADED + } + INITIATOR_TYPE_MAP = {0: constants.InitiatorType.UNKNOWN, + 1: constants.InitiatorType.FC, + 2: constants.InitiatorType.ISCSI + } + def __init__(self, **kwargs): super().__init__(**kwargs) self.rest_handler = rest_handler.RestHandler(**kwargs) @@ -709,6 +737,131 @@ def clear_alert(self, context, alert): def get_access_url(): return 'https://{ip}' + def list_storage_host_initiators(self, context): + try: + initiator_list = [] + page = 1 + while True: + initiators = self.rest_handler.get_host_initiators(page) + if not initiators: + return initiator_list + if 'entries' not in initiators or \ + len(initiators['entries']) < 1: + break + init_entries = initiators.get('entries') + for initiator in init_entries: + content = initiator.get('content') + if not content: + continue + health_value = content.get('health', {}).get('value') + status = UnityStorDriver.INITIATOR_STATUS_MAP.get( + health_value, + constants.InitiatorStatus.UNKNOWN + ) + init_result = { + "name": content.get('initiatorId'), + "storage_id": self.storage_id, + "native_storage_host_initiator_id": content.get('id'), + "wwn": content.get('initiatorId'), + "status": status, + "type": UnityStorDriver.INITIATOR_TYPE_MAP.get( + content.get('type')), + "native_storage_host_id": content.get( + 'parentHost', {}).get('id') + } + initiator_list.append(init_result) + page += 1 + return initiator_list + except Exception as e: + LOG.error("Failed to get initiators from unity") + raise e + + def list_storage_hosts(self, context): + try: + host_list = [] + page = 1 + while True: + hosts = self.rest_handler.get_all_hosts(page) + if not hosts: + return host_list + if 'entries' not in hosts or len(hosts['entries']) < 1: + break + ips = self.rest_handler.get_host_ip() + host_entries = hosts.get('entries') + for host in host_entries: + host_ip = None + content = host.get('content') + if not content: + continue + health_value = content.get('health', {}).get('value') + status = UnityStorDriver.HOST_STATUS_MAP.get( + health_value, + constants.HostStatus.OFFLINE + ) + if ips: + ip_entries = ips.get('entries') + for ip in ip_entries: + ip_content = ip.get('content') + if not ip_content: + continue + if ip_content.get('host', {}).get('id') \ + == content.get('id'): + host_ip = ip_content.get('address') + break + if content.get('osType'): + if 'VMware ESXi' in content.get('osType'): + os_type = constants.HostOSTypes.VMWARE_ESX + else: + os_type = UnityStorDriver.OS_TYPE_MAP.get( + content.get('osType'), + constants.HostOSTypes.UNKNOWN) + else: + os_type = None + host_result = { + "name": content.get('name'), + "description": content.get('description'), + "storage_id": self.storage_id, + "native_storage_host_id": content.get('id'), + "os_type": os_type, + "status": status, + "ip_address": host_ip + } + host_list.append(host_result) + page += 1 + return host_list + except Exception as e: + LOG.error("Failed to get host metrics from unity") + raise e + + def list_masking_views(self, context): + try: + view_list = [] + page = 1 + while True: + views = self.rest_handler.get_host_lun(page) + if not views: + return view_list + if 'entries' not in views or len(views['entries']) < 1: + break + view_entries = views.get('entries') + for view in view_entries: + content = view.get('content') + view_result = { + "name": content.get('id'), + "native_storage_host_id": + content.get('host', {}).get('id'), + "storage_id": self.storage_id, + "native_volume_id": content.get('lun', {}).get('id'), + "native_masking_view_id": content.get('id'), + } + view_list.append(view_result) + page += 1 + return view_list + + except Exception as e: + LOG.error("Failed to get view metrics from unity") + raise e + def get_metrics_loop(self, target, start_time, end_time, metrics, path): page = 1 diff --git a/delfin/tests/unit/drivers/dell_emc/unity/test_emc_unity.py b/delfin/tests/unit/drivers/dell_emc/unity/test_emc_unity.py index aa0901481..445535d67 100644 --- a/delfin/tests/unit/drivers/dell_emc/unity/test_emc_unity.py +++ b/delfin/tests/unit/drivers/dell_emc/unity/test_emc_unity.py @@ -2245,6 +2245,291 @@ 'readIoSize', 'writeIoSize' ] } +GET_ALL_INIT = { + "entries": [ + { + "content": { + "id": "init1", + "type": 1, + "initiatorId": "fs1", + "path": "/", + "health": { + "value": 5, + "descriptionIds": [ + "ALRT_COMPONENT_OK" + ], + "descriptions": [ + "The component is operating normally." + ] + }, + "parentHost": { + "id": "fs_1" + } + } + }, + { + "content": { + "id": "init14", + "type": 1, + "initiatorId": "boga", + "path": "/", + "health": { + "value": 5, + "descriptionIds": [ + "ALRT_COMPONENT_OK" + ], + "descriptions": [ + "The component is operating normally." + ] + }, + "parentHost": { + "id": "fs_16" + } + } + }, + { + "content": { + "id": "init11", + "type": 2, + "initiatorId": "fs2", + "health": { + "value": 5, + "descriptionIds": [ + "ALRT_COMPONENT_OK" + ], + "descriptions": [ + "The component is operating normally." + ] + }, + "parentHost": { + "id": "host_20" + } + } + } + ] +} +GET_ALL_INIT_NULL = { + "entries": [ + ] +} +INIT_RESULT = [ + { + 'name': 'fs1', + 'storage_id': '12345', + 'native_storage_host_initiator_id': 'init1', + 'wwn': 'fs1', + 'status': 'online', + 'type': 'fc', + 'native_storage_host_id': 'fs_1' + }, { + 'name': 'boga', + 'storage_id': '12345', + 'native_storage_host_initiator_id': 'init14', + 'wwn': 'boga', + 'status': 'online', + 'type': 'fc', + 'native_storage_host_id': 'fs_16' + }, { + 'name': 'fs2', + 'storage_id': '12345', + 'native_storage_host_initiator_id': 'init11', + 'wwn': 'fs2', + 'status': 'online', + 'type': 'iscsi', + 'native_storage_host_id': 'host_20' + } +] +GET_ALL_HOST = { + "entries": [ + { + "content": { + "id": "host1", + "type": 1, + "name": "fs1", + "description": "test", + "osType": "AIX", + "health": { + "value": 5, + "descriptionIds": [ + "ALRT_COMPONENT_OK" + ], + "descriptions": [ + "The component is operating normally." + ] + }, + "parentHost": { + "id": "fs_1" + } + } + }, + { + "content": { + "id": "host2", + "type": 1, + "name": "boga", + "description": "test", + "osType": "Citrix XenServer", + "health": { + "value": 5, + "descriptionIds": [ + "ALRT_COMPONENT_OK" + ], + "descriptions": [ + "The component is operating normally." + ] + }, + "parentHost": { + "id": "fs_16" + } + } + }, + { + "content": { + "id": "host3", + "type": 2, + "name": "fs2", + "description": "test", + "osType": "VMware ESXi 6.5", + "health": { + "value": 5, + "descriptionIds": [ + "ALRT_COMPONENT_OK" + ], + "descriptions": [ + "The component is operating normally." + ] + }, + "parentHost": { + "id": "host_20" + } + } + } + ] +} +GET_ALL_HOST_NULL = { + "entries": [ + ] +} +GET_HOST_IP = { + "entries": [ + { + "content": { + "id": "ip1", + "address": "1.1.1.1", + "host": { + "id": "host1" + } + } + }, + { + "content": { + "id": "ip1", + "address": "1.1.1.2", + "host": { + "id": "host2" + } + } + }, + { + "content": { + "id": "ip1", + "address": "1.1.1.1", + "host": { + "id": "host3" + } + } + } + ] +} +HOST_RESULT = [ + { + 'name': 'fs1', + 'description': 'test', + 'storage_id': '12345', + 'native_storage_host_id': 'host1', + 'os_type': 'AIX', + 'status': 'normal', + 'ip_address': '1.1.1.1' + }, { + 'name': 'boga', + 'description': 'test', + 'storage_id': '12345', + 'native_storage_host_id': 'host2', + 'os_type': 'XenServer', + 'status': 'normal', + 'ip_address': '1.1.1.2' + }, { + 'name': 'fs2', + 'description': 'test', + 'storage_id': '12345', + 'native_storage_host_id': 'host3', + 'os_type': 'VMware ESX', + 'status': 'normal', + 'ip_address': '1.1.1.1' + } +] +GET_HOST_LUN = { + "entries": [ + { + "content": { + "id": "1", + "lun": { + "id": "lun1" + }, + "host": { + "id": "host1" + } + } + }, + { + "content": { + "id": "2", + "lun": { + "id": "lun2" + }, + "host": { + "id": "host2" + } + } + }, + { + "content": { + "id": "3", + "lun": { + "id": "lun3" + }, + "host": { + "id": "host3" + } + } + } + ] +} +GET_HOST_LUN_NULL = { + "entries": [ + ] +} +VIEW_RESULT = [ + { + 'name': '1', + 'native_storage_host_id': 'host1', + 'storage_id': '12345', + 'native_volume_id': 'lun1', + 'native_masking_view_id': '1' + }, { + 'name': '2', + 'native_storage_host_id': 'host2', + 'storage_id': '12345', + 'native_volume_id': 'lun2', + 'native_masking_view_id': '2' + }, { + 'name': '3', + 'native_storage_host_id': 'host3', + 'storage_id': '12345', + 'native_volume_id': 'lun3', + 'native_masking_view_id': '3' + } +] class TestUNITYStorDriver(TestCase): @@ -2465,3 +2750,27 @@ def test_latest_perf_timestamp(self, mock_history): last_time = UnityStorDriver(**ACCESS_INFO).get_latest_perf_timestamp( context) self.assertEqual(last_time, 1625726830000) + + @mock.patch.object(RestHandler, 'get_host_initiators') + def test_host_initiators(self, mock_init): + RestHandler.login = mock.Mock(return_value=None) + mock_init.side_effect = [GET_ALL_INIT, GET_ALL_INIT_NULL] + initiators = UnityStorDriver( + **ACCESS_INFO).list_storage_host_initiators(context) + self.assertEqual(initiators, INIT_RESULT) + + @mock.patch.object(RestHandler, 'get_all_hosts') + @mock.patch.object(RestHandler, 'get_host_ip') + def test_hosts(self, mock_ip, mock_host): + RestHandler.login = mock.Mock(return_value=None) + mock_host.side_effect = [GET_ALL_HOST, GET_ALL_HOST_NULL] + mock_ip.return_value = GET_HOST_IP + hosts = UnityStorDriver(**ACCESS_INFO).list_storage_hosts(context) + self.assertEqual(hosts, HOST_RESULT) + + @mock.patch.object(RestHandler, 'get_host_lun') + def test_masking_views(self, mock_view): + RestHandler.login = mock.Mock(return_value=None) + mock_view.side_effect = [GET_HOST_LUN, GET_HOST_LUN_NULL] + views = UnityStorDriver(**ACCESS_INFO).list_masking_views(context) + self.assertEqual(views, VIEW_RESULT)