From f3f5510be08af06b8d630108a1447b3b35db3395 Mon Sep 17 00:00:00 2001 From: UtkarshShah0 <93548048+UtkarshShah0@users.noreply.github.com> Date: Thu, 21 Apr 2022 22:36:47 +0530 Subject: [PATCH 01/17] Fixed issue #873 Fixed the return value in the function get_remote_file_to_xml --- delfin/drivers/utils/tools.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delfin/drivers/utils/tools.py b/delfin/drivers/utils/tools.py index d77bf4e12..93a172995 100644 --- a/delfin/drivers/utils/tools.py +++ b/delfin/drivers/utils/tools.py @@ -136,4 +136,4 @@ def get_remote_file_to_xml(ssh, file, local_path, remote_path): finally: if os.path.exists(local_file): Tools.remove_file_with_same_type(file, local_path) - return root_node + return root_node From 77fb689d4664e8f0e17e31940271375017a28ec8 Mon Sep 17 00:00:00 2001 From: Yash <93548927+code4Y@users.noreply.github.com> Date: Thu, 21 Apr 2022 23:08:03 +0530 Subject: [PATCH 02/17] Removed inappropriate operator (And) #872 And operator and duplicate host was removed --- delfin/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delfin/test.py b/delfin/test.py index 39e6bc5b3..cac4802e9 100644 --- a/delfin/test.py +++ b/delfin/test.py @@ -153,7 +153,7 @@ def flags(self, **kw): CONF.set_override(k, v) def start_service(self, name, host=None, **kwargs): - host = host and host or uuidutils.generate_uuid() + host = host or uuidutils.generate_uuid() kwargs.setdefault('host', host) kwargs.setdefault('binary', 'delfin-%s' % name) svc = service.Service.create(**kwargs) From 2f3bccd14ddba89fd32c029d443f5f72de1ab6f5 Mon Sep 17 00:00:00 2001 From: nikita15p Date: Fri, 22 Apr 2022 11:06:57 +0530 Subject: [PATCH 03/17] fixed 871 Signed-off-by: nikita15p --- delfin/tests/unit/api/fakes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/delfin/tests/unit/api/fakes.py b/delfin/tests/unit/api/fakes.py index d61e62a45..00b928658 100644 --- a/delfin/tests/unit/api/fakes.py +++ b/delfin/tests/unit/api/fakes.py @@ -329,7 +329,7 @@ def fake_access_infos_show_all(context): return [access_info] -def fake_update_access_info(self, context, access_info): +def fake_update_access_info(self, context): access_info = models.AccessInfo() access_info.updated_at = '2020-06-15T09:50:31.698956' From ac3ec46b40c0e264953f6469dfa4e2c0b3c98eeb Mon Sep 17 00:00:00 2001 From: UtkarshShah0 <93548048+UtkarshShah0@users.noreply.github.com> Date: Fri, 22 Apr 2022 15:32:32 +0530 Subject: [PATCH 04/17] updated_post_install_verification Fixed issue #840 Added post install verification step --- installer/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/installer/README.md b/installer/README.md index d26f74387..2dc362620 100644 --- a/installer/README.md +++ b/installer/README.md @@ -131,6 +131,13 @@ $ installer/install Note: Multiple instances of exporter and api is not allowed currently. +#### Post install verification +After delfin installation use the following command to verify all process +of delfin are running. +```sh +ps -aux | grep delfin +``` + # Uninstall Running the uninstall script will stop all delfin processes and do cleanup ```sh From 5286cd7b42cd3b76f70e22b7e7b68972743d8657 Mon Sep 17 00:00:00 2001 From: Navaneetha167 <103172664+Navaneetha167@users.noreply.github.com> Date: Tue, 26 Apr 2022 19:31:07 +0530 Subject: [PATCH 05/17] the updated swagger file error is in snmp_config --- openapi-spec/swagger.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openapi-spec/swagger.yaml b/openapi-spec/swagger.yaml index 538ac832e..619e772ad 100644 --- a/openapi-spec/swagger.yaml +++ b/openapi-spec/swagger.yaml @@ -3539,7 +3539,7 @@ components: description: Response for all access infos configuration. type: object properties: - snmp_configs: + access_infos: type: array description: the list of access info items: From 5618992adf54cc3beef0dc0896564744a556539b Mon Sep 17 00:00:00 2001 From: Yash <93548927+code4Y@users.noreply.github.com> Date: Wed, 27 Apr 2022 10:57:10 +0530 Subject: [PATCH 06/17] Comment updated for Fakedriver - for issue #888 Added comments explaining why Fakedriver methods are empty : add_trap_config remove_trap_config parse_alert clear_alert --- delfin/drivers/fake_storage/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/delfin/drivers/fake_storage/__init__.py b/delfin/drivers/fake_storage/__init__.py index fd2a3a179..db58d3e5f 100644 --- a/delfin/drivers/fake_storage/__init__.py +++ b/delfin/drivers/fake_storage/__init__.py @@ -422,17 +422,17 @@ def list_shares(self, ctx): return share_list def add_trap_config(self, context, trap_config): - pass + pass # Fakedriver do not require to add trap config def remove_trap_config(self, context, trap_config): - pass + pass # Fakedriver do not require to remove trap config @staticmethod def parse_alert(context, alert): - pass + pass # Fakedriver do not require to parse alert def clear_alert(self, context, alert): - pass + pass # Fakedriver do not require to clear alert def list_alerts(self, context, query_para=None): alert_list = [{ From a001e742d82125bc094cad7dcb9678645d5223f6 Mon Sep 17 00:00:00 2001 From: Yash <93548927+code4Y@users.noreply.github.com> Date: Wed, 27 Apr 2022 17:58:02 +0530 Subject: [PATCH 07/17] Added spaces before comments Added two spaces before comments --- delfin/drivers/fake_storage/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/delfin/drivers/fake_storage/__init__.py b/delfin/drivers/fake_storage/__init__.py index db58d3e5f..fe43ec95c 100644 --- a/delfin/drivers/fake_storage/__init__.py +++ b/delfin/drivers/fake_storage/__init__.py @@ -422,17 +422,17 @@ def list_shares(self, ctx): return share_list def add_trap_config(self, context, trap_config): - pass # Fakedriver do not require to add trap config + pass # Fakedriver do not require to add trap config def remove_trap_config(self, context, trap_config): - pass # Fakedriver do not require to remove trap config + pass # Fakedriver do not require to remove trap config @staticmethod def parse_alert(context, alert): - pass # Fakedriver do not require to parse alert + pass # Fakedriver do not require to parse alert def clear_alert(self, context, alert): - pass # Fakedriver do not require to clear alert + pass # Fakedriver do not require to clear alert def list_alerts(self, context, query_para=None): alert_list = [{ From e8066026ff278ce53ba89b750b7ccacb6beaf468 Mon Sep 17 00:00:00 2001 From: tanjiangyu-ghca <79631193+tanjiangyu-ghca@users.noreply.github.com> Date: Thu, 28 Apr 2022 15:57:18 +0800 Subject: [PATCH 08/17] ds8000 add host mapping (#822) --- delfin/drivers/ibm/ds8k/consts.py | 2 + delfin/drivers/ibm/ds8k/ds8k.py | 86 ++++++++++++++++- .../drivers/ibm/ibm_ds8k/test_ibm_ds8k.py | 96 +++++++++++++++++++ 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 delfin/drivers/ibm/ds8k/consts.py diff --git a/delfin/drivers/ibm/ds8k/consts.py b/delfin/drivers/ibm/ds8k/consts.py new file mode 100644 index 000000000..d7d907371 --- /dev/null +++ b/delfin/drivers/ibm/ds8k/consts.py @@ -0,0 +1,2 @@ +HOST_PORT_URL = '/api/v1/host_ports' +HOST_URL = '/api/v1/hosts' diff --git a/delfin/drivers/ibm/ds8k/ds8k.py b/delfin/drivers/ibm/ds8k/ds8k.py index 41b25fe09..550109626 100644 --- a/delfin/drivers/ibm/ds8k/ds8k.py +++ b/delfin/drivers/ibm/ds8k/ds8k.py @@ -18,7 +18,7 @@ from delfin import exception from delfin.common import constants from delfin.drivers import driver -from delfin.drivers.ibm.ds8k import rest_handler, alert_handler +from delfin.drivers.ibm.ds8k import rest_handler, alert_handler, consts LOG = log.getLogger(__name__) @@ -35,6 +35,10 @@ class DS8KDriver(driver.StorageDriver): 'fenced': constants.PortHealthStatus.UNKNOWN, 'quiescing': constants.PortHealthStatus.UNKNOWN } + INITIATOR_STATUS_MAP = {'logged in': constants.InitiatorStatus.ONLINE, + 'logged out': constants.InitiatorStatus.OFFLINE, + 'unconfigured': constants.InitiatorStatus.UNKNOWN + } def __init__(self, **kwargs): super().__init__(**kwargs) @@ -259,3 +263,83 @@ def clear_alert(self, context, alert): @staticmethod def get_access_url(): return 'https://{ip}:{port}' + + def list_storage_hosts(self, context): + try: + host_list = [] + hosts = self.rest_handler.get_rest_info(consts.HOST_URL) + if not hosts: + return host_list + host_entries = hosts.get('data', {}).get('hosts', []) + for host in host_entries: + status = constants.HostStatus.NORMAL if \ + host.get('state') == 'online' else \ + constants.HostStatus.OFFLINE + os_type = constants.HostOSTypes.VMWARE_ESX if \ + host.get('hosttype') == 'VMware' else \ + constants.HostOSTypes.UNKNOWN + host_result = { + "name": host.get('name'), + "storage_id": self.storage_id, + "native_storage_host_id": host.get('name'), + "os_type": os_type, + "status": status + } + host_list.append(host_result) + return host_list + except Exception as e: + LOG.error("Failed to get hosts from ds8k") + raise e + + def list_masking_views(self, context): + try: + view_list = [] + hosts = self.rest_handler.get_rest_info(consts.HOST_URL) + if not hosts: + return view_list + host_entries = hosts.get('data', {}).get('hosts', []) + for host in host_entries: + view_url = '%s/%s/mappings' % (consts.HOST_URL, + host.get('name')) + views = self.rest_handler.get_rest_info(view_url) + if not views: + continue + view_entries = views.get('data', {}).get('mappings', []) + for view in view_entries: + view_id = '%s_%s' % (view.get('lunid'), host.get('name')) + view_result = { + "name": view_id, + "native_storage_host_id": host.get('name'), + "storage_id": self.storage_id, + "native_volume_id": view.get('volume', {}).get('id'), + "native_masking_view_id": view_id, + } + view_list.append(view_result) + return view_list + except Exception as e: + LOG.error("Failed to get views from ds8k") + raise e + + def list_storage_host_initiators(self, context): + try: + initiator_list = [] + host_ports = self.rest_handler.get_rest_info(consts.HOST_PORT_URL) + if not host_ports: + return initiator_list + port_entries = host_ports.get('data', {}).get('host_ports', []) + for port in port_entries: + status = DS8KDriver.INITIATOR_STATUS_MAP.get(port.get('state')) + init_result = { + "name": port.get('wwpn'), + "storage_id": self.storage_id, + "native_storage_host_initiator_id": port.get('wwpn'), + "wwn": port.get('wwpn'), + "status": status, + "type": constants.InitiatorType.UNKNOWN, + "native_storage_host_id": port.get('host', {}).get('name') + } + initiator_list.append(init_result) + return initiator_list + except Exception as e: + LOG.error("Failed to get initiators from ds8k") + raise e diff --git a/delfin/tests/unit/drivers/ibm/ibm_ds8k/test_ibm_ds8k.py b/delfin/tests/unit/drivers/ibm/ibm_ds8k/test_ibm_ds8k.py index e3dcd7936..145608c03 100644 --- a/delfin/tests/unit/drivers/ibm/ibm_ds8k/test_ibm_ds8k.py +++ b/delfin/tests/unit/drivers/ibm/ibm_ds8k/test_ibm_ds8k.py @@ -344,6 +344,80 @@ 'resource_type': 'Storage', 'location': 'eeeeeeeee' } +GET_INITORATORS = { + "data": { + "host_ports": + [ + { + "wwpn": "50050763030813A2", + "state": "logged in", + "hosttype": "VMware", + "addrdiscovery": "lunpolling", + "lbs": "512", + "host": { + "name": "myhost" + } + } + ] + } +} +INIT_RESULT = [ + { + 'name': '50050763030813A2', + 'storage_id': '12345', + 'native_storage_host_initiator_id': '50050763030813A2', + 'wwn': '50050763030813A2', + 'status': 'online', + 'type': 'unknown', + 'native_storage_host_id': 'myhost' + } +] +GET_ALL_HOSTS = { + "data": { + "hosts": + [ + { + "name": "test_host", + "state": "online", + "hosttype": "VMware", + "addrmode": "SCSI mask", + "addrdiscovery": "lunpolling", + "lbs": "512" + } + ] + } +} +HOST_RESULT = [ + { + 'name': 'test_host', + 'storage_id': '12345', + 'native_storage_host_id': 'test_host', + 'os_type': 'VMware ESX', + 'status': 'normal' + } +] +GET_HOST_MAPPING = { + "data": { + "mappings": + [ + { + "lunid": "00", + "volume": { + "id": "0005" + } + } + ] + } +} +VIEW_RESULT = [ + { + 'name': '00_test_host', + 'native_storage_host_id': 'test_host', + 'storage_id': '12345', + 'native_volume_id': '0005', + 'native_masking_view_id': '00_test_host' + } +] class TestDS8KDriver(TestCase): @@ -401,3 +475,25 @@ def test_list_list_controllers(self, mock_contrl): mock_contrl.return_value = GET_ALL_CONTROLLERS controller = DS8KDriver(**ACCESS_INFO).list_controllers(context) self.assertEqual(controller, contrl_result) + + @mock.patch.object(RestHandler, 'get_rest_info') + def test_host_initiators(self, mock_init): + RestHandler.login = mock.Mock(return_value=None) + mock_init.return_value = GET_INITORATORS + initiators = DS8KDriver( + **ACCESS_INFO).list_storage_host_initiators(context) + self.assertEqual(initiators, INIT_RESULT) + + @mock.patch.object(RestHandler, 'get_rest_info') + def test_hosts(self, mock_host): + RestHandler.login = mock.Mock(return_value=None) + mock_host.return_value = GET_ALL_HOSTS + hosts = DS8KDriver(**ACCESS_INFO).list_storage_hosts(context) + self.assertEqual(hosts, HOST_RESULT) + + @mock.patch.object(RestHandler, 'get_rest_info') + def test_masking_views(self, mock_view): + RestHandler.login = mock.Mock(return_value=None) + mock_view.side_effect = [GET_ALL_HOSTS, GET_HOST_MAPPING] + views = DS8KDriver(**ACCESS_INFO).list_masking_views(context) + self.assertEqual(views, VIEW_RESULT) From f5a82ab9928aa4db93fe8fab7504824ce0f48833 Mon Sep 17 00:00:00 2001 From: tanjiangyu-ghca <79631193+tanjiangyu-ghca@users.noreply.github.com> Date: Thu, 28 Apr 2022 16:18:44 +0800 Subject: [PATCH 09/17] vsp add host view mapping (#810) --- delfin/drivers/hitachi/vsp/consts.py | 2 +- delfin/drivers/hitachi/vsp/rest_handler.py | 42 ++- delfin/drivers/hitachi/vsp/vsp_stor.py | 267 ++++++++++++++++++ .../hitachi/vsp/test_hitachi_vspstor.py | 161 +++++++++++ 4 files changed, 467 insertions(+), 5 deletions(-) diff --git a/delfin/drivers/hitachi/vsp/consts.py b/delfin/drivers/hitachi/vsp/consts.py index b8538606e..29bd9b98e 100644 --- a/delfin/drivers/hitachi/vsp/consts.py +++ b/delfin/drivers/hitachi/vsp/consts.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -SOCKET_TIMEOUT = 90 +SOCKET_TIMEOUT = 180 ERROR_SESSION_INVALID_CODE = 403 ERROR_SESSION_IS_BEING_USED_CODE = 409 BLOCK_SIZE = 512 diff --git a/delfin/drivers/hitachi/vsp/rest_handler.py b/delfin/drivers/hitachi/vsp/rest_handler.py index 55e34fc0b..7d85fc397 100644 --- a/delfin/drivers/hitachi/vsp/rest_handler.py +++ b/delfin/drivers/hitachi/vsp/rest_handler.py @@ -177,14 +177,14 @@ def get_device_id(self): system_info = storage_systems.get('data') for system in system_info: succeed = True - if system.get('model') in consts.SUPPORTED_VSP_SERIES: - if system.get('ctl1Ip') == self.rest_host or \ - system.get('ctl2Ip') == self.rest_host: + if system.get('svpIp'): + if system.get('svpIp') == self.rest_host: self.storage_device_id = system.get('storageDeviceId') self.device_model = system.get('model') self.serial_number = system.get('serialNumber') break - elif system.get('svpIp') == self.rest_host: + elif system.get('ctl1Ip') == self.rest_host or \ + system.get('ctl2Ip') == self.rest_host: self.storage_device_id = system.get('storageDeviceId') self.device_model = system.get('model') self.serial_number = system.get('serialNumber') @@ -261,3 +261,37 @@ def get_alerts(self, param, start, end): param, start, end) result_json = self.get_rest_info(url) return result_json + + def get_all_host_groups(self): + url = '%s/%s/host-groups' % \ + (RestHandler.COMM_URL, self.storage_device_id) + result_json = self.get_rest_info(url) + return result_json + + def get_specific_host_group(self, port_id): + url = '%s/%s/host-groups?portId=%s' % \ + (RestHandler.COMM_URL, self.storage_device_id, port_id) + result_json = self.get_rest_info(url) + return result_json + + def get_host_wwn(self, port_id, group_number): + url = '%s/%s/host-wwns?portId=%s&hostGroupNumber=%s' % \ + (RestHandler.COMM_URL, self.storage_device_id, port_id, + group_number) + result_json = self.get_rest_info(url) + return result_json + + def get_iscsi_name(self, port_id, group_number): + url = '%s/%s/host-iscsis?portId=%s&hostGroupNumber=%s' % \ + (RestHandler.COMM_URL, self.storage_device_id, port_id, + group_number) + result_json = self.get_rest_info(url) + return result_json + + def get_lun_path(self, port_id, group_number): + url = '%s/%s/luns?portId=%s&hostGroupNumber=%s&' \ + 'isBasicLunInformation=true' % \ + (RestHandler.COMM_URL, self.storage_device_id, port_id, + group_number) + result_json = self.get_rest_info(url) + return result_json diff --git a/delfin/drivers/hitachi/vsp/vsp_stor.py b/delfin/drivers/hitachi/vsp/vsp_stor.py index 82bcdfb92..87f4f9de3 100644 --- a/delfin/drivers/hitachi/vsp/vsp_stor.py +++ b/delfin/drivers/hitachi/vsp/vsp_stor.py @@ -64,6 +64,18 @@ class HitachiVspDriver(driver.StorageDriver): "HNASS": constants.PortType.OTHER, "HNASU": constants.PortType.OTHER } + OS_TYPE_MAP = {"HP-UX": constants.HostOSTypes.HP_UX, + "SOLARIS": constants.HostOSTypes.SOLARIS, + "AIX": constants.HostOSTypes.AIX, + "WIN": constants.HostOSTypes.WINDOWS, + "LINUX/IRIX": constants.HostOSTypes.LINUX, + "TRU64": constants.HostOSTypes.UNKNOWN, + "OVMS": constants.HostOSTypes.OPEN_VMS, + "NETWARE": constants.HostOSTypes.UNKNOWN, + "VMWARE": constants.HostOSTypes.VMWARE_ESX, + "VMWARE_EX": constants.HostOSTypes.VMWARE_ESX, + "WIN_EX": constants.HostOSTypes.WINDOWS + } DISK_STATUS_TYPE = {"NML": constants.DiskStatus.NORMAL, "CPY": constants.DiskStatus.NORMAL, "CPI": constants.DiskStatus.NORMAL, @@ -501,3 +513,258 @@ def parse_alert(context, alert): def clear_alert(self, context, alert): pass + + @staticmethod + def handle_group_with_port(group_info): + group_list = {} + if not group_info: + return group_list + group_entries = group_info.get('data') + for group in group_entries: + if group_list.get(group.get('portId')): + group_list[group.get('portId')].append( + group.get('hostGroupNumber')) + else: + group_list[group.get('portId')] = [] + group_list[group.get('portId')].append( + group.get('hostGroupNumber')) + return group_list + + @staticmethod + def get_host_info(data, storage_id, host_list, type, os_type): + if data: + host_entries = data.get('data') + if not host_entries: + return True + for host in host_entries: + if type == 'iscsi': + host_id = host.get('hostIscsiId') + host_name = host.get('iscsiNickname') if \ + host.get('iscsiNickname') != '-' \ + else host.get('iscsiName') + else: + host_id = host.get('hostWwnId') + host_name = host.get('wwnNickname') if \ + host.get('wwnNickname') != '-' \ + else host.get('hostWwn') + host_result = { + "name": host_name, + "storage_id": storage_id, + "native_storage_host_id": host_id.replace(",", "_"), + "os_type": os_type, + "status": constants.HostStatus.NORMAL + } + host_list.append(host_result) + return True + + def list_storage_hosts(self, context): + try: + host_groups = self.rest_handler.get_all_host_groups() + host_list = [] + if not host_groups: + return host_list + group_with_port = HitachiVspDriver.handle_group_with_port( + host_groups) + for port in group_with_port: + kwargs = { + 'method': 'host', + 'port': port, + 'result': host_list + } + self.handle_san_info(**kwargs) + return host_list + except Exception as e: + LOG.error("Failed to get host from vsp") + raise e + + @staticmethod + def get_initiator_from_host(data, storage_id, initiator_list, type): + if data: + host_entries = data.get('data') + if not host_entries: + return True + for host in host_entries: + if type == 'iscsi': + initiator_id = host.get('hostIscsiId') + init_type = constants.InitiatorType.ISCSI + init_name = host.get('iscsiName') + else: + initiator_id = host.get('hostWwnId') + init_type = constants.InitiatorType.FC + init_name = host.get('hostWwn') + for initiator in initiator_list: + if initiator.get('wwn') == init_name: + continue + init_result = { + "name": init_name, + "storage_id": storage_id, + "native_storage_host_initiator_id": init_name, + "wwn": init_name, + "status": constants.InitiatorStatus.ONLINE, + "type": init_type, + "alias": host.get('portId'), + "native_storage_host_id": initiator_id.replace(",", "_") + } + initiator_list.append(init_result) + return True + + def list_storage_host_initiators(self, context): + try: + initiator_list = [] + host_groups = self.rest_handler.get_all_host_groups() + if not host_groups: + return initiator_list + group_with_port = HitachiVspDriver.handle_group_with_port( + host_groups) + for port in group_with_port: + kwargs = { + 'method': 'initator', + 'port': port, + 'result': initiator_list + } + self.handle_san_info(**kwargs) + return initiator_list + except Exception as e: + LOG.error("Failed to get initiators from vsp") + raise e + + @staticmethod + def get_host_ids(data, target, host_ids, host_grp_relation_list, + storage_id, group_id): + if data: + host_entries = data.get('data') + if not host_entries: + return True + for host in host_entries: + if host.get(target): + host_ids.append(host.get(target).replace(",", "_")) + relation = { + 'storage_id': storage_id, + 'native_storage_host_group_id': group_id, + 'native_storage_host_id': + host.get(target).replace(",", "_") + } + host_grp_relation_list.append(relation) + + def list_storage_host_groups(self, context): + try: + host_groups = self.rest_handler.get_all_host_groups() + host_group_list = [] + host_grp_relation_list = [] + if not host_groups: + return host_group_list + group_with_port = HitachiVspDriver.handle_group_with_port( + host_groups) + for port in group_with_port: + kwargs = { + 'method': 'group', + 'port': port, + 'result': host_grp_relation_list, + 'group_list': host_group_list + } + self.handle_san_info(**kwargs) + result = { + 'storage_host_groups': host_group_list, + 'storage_host_grp_host_rels': host_grp_relation_list + } + return result + except Exception: + LOG.error("Failed to get host_groups from vsp") + raise + + def handle_lun_path(self, **kwargs): + view_list = [] + views = self.rest_handler.get_lun_path( + kwargs.get('port'), kwargs.get('group')) + if not views: + return None + view_entries = views.get('data') + if not view_entries: + return None + for view in view_entries: + group_id = '%s_%s' % (view.get('portId'), + view.get('hostGroupNumber')) + view_result = { + "name": view.get('lunId'), + "native_storage_host_group_id": group_id, + "storage_id": self.storage_id, + "native_volume_id": HitachiVspDriver.to_vsp_lun_id_format( + view.get('ldevId')), + "native_masking_view_id": view.get('lunId').replace(",", "_"), + } + kwargs.get('result').append(view_result) + return view_list + + def list_masking_views(self, context): + try: + view_list = [] + host_groups = self.rest_handler.get_all_host_groups() + if not host_groups: + return view_list + group_data = host_groups.get('data') + for group in group_data: + kwargs = { + 'group': group.get('hostGroupNumber'), + 'port': group.get('portId'), + 'result': view_list + } + self.handle_lun_path(**kwargs) + return view_list + except Exception as e: + LOG.error("Failed to get views from vsp") + raise e + + def handle_san_info(self, **kwargs): + groups = self.rest_handler.get_specific_host_group( + kwargs.get('port')) + group_data = groups.get('data') + for specific_group in group_data: + iscsis = None + wwns = None + if specific_group.get('iscsiName'): + iscsis = self.rest_handler.get_iscsi_name( + specific_group.get('portId'), + specific_group.get('hostGroupNumber')) + else: + wwns = self.rest_handler.get_host_wwn( + specific_group.get('portId'), + specific_group.get('hostGroupNumber')) + if kwargs.get('method') == 'host': + os_type = HitachiVspDriver.OS_TYPE_MAP.get( + specific_group.get('hostMode'), + constants.HostOSTypes.UNKNOWN) + if specific_group.get('iscsiName'): + HitachiVspDriver.get_host_info( + iscsis, self.storage_id, kwargs.get('result'), + 'iscsi', os_type) + else: + HitachiVspDriver.get_host_info( + wwns, self.storage_id, + kwargs.get('result'), 'fc', os_type) + elif kwargs.get('method') == 'group': + host_ids = [] + group_id = specific_group.get('hostGroupId').replace(",", "_") + if specific_group.get('iscsiName'): + HitachiVspDriver.get_host_ids( + iscsis, 'hostIscsiId', host_ids, + kwargs.get('result'), self.storage_id, + group_id) + else: + HitachiVspDriver.get_host_ids( + wwns, 'hostWwnId', host_ids, + kwargs.get('result'), self.storage_id, + group_id) + group_result = { + "name": specific_group.get('hostGroupName'), + "storage_id": self.storage_id, + "native_storage_host_group_id": group_id, + "storage_hosts": ','.join(host_ids) + } + kwargs.get('group_list').append(group_result) + else: + if specific_group.get('iscsiName'): + HitachiVspDriver.get_initiator_from_host( + iscsis, self.storage_id, kwargs.get('result'), 'iscsi') + else: + HitachiVspDriver.get_initiator_from_host( + wwns, self.storage_id, kwargs.get('result'), 'fc') diff --git a/delfin/tests/unit/drivers/hitachi/vsp/test_hitachi_vspstor.py b/delfin/tests/unit/drivers/hitachi/vsp/test_hitachi_vspstor.py index e235501ac..a00e7ecb2 100644 --- a/delfin/tests/unit/drivers/hitachi/vsp/test_hitachi_vspstor.py +++ b/delfin/tests/unit/drivers/hitachi/vsp/test_hitachi_vspstor.py @@ -454,6 +454,123 @@ def __init__(self): 'ipv4_mask': '255.255.0.0', 'ipv6': None }] +GET_ALL_GROUPS = { + "data": [ + { + "hostGroupId": "CL1-A,0", + "portId": "CL1-A", + "hostGroupNumber": 0, + "hostGroupName": "1A-G00", + "hostMode": "LINUX/IRIX" + } + ] +} +GET_SINGLE_WWN_GROUP = { + "data": [ + { + "hostGroupId": "CL1-A,0", + "portId": "CL1-A", + "hostGroupNumber": 0, + "hostGroupName": "1A-G00", + "hostMode": "LINUX/IRIX" + } + ] +} +GET_SINGLE_ISCSI_GROUP = { + "data": [ + { + "hostGroupId": "CL1-A,0", + "portId": "CL1-A", + "hostGroupNumber": 0, + "hostGroupName": "1A-G00", + "hostMode": "LINUX/IRIX", + "iscsiName": "iqn.ewdhehdhdhh" + } + ] +} +GET_HOST_WWN = { + "data": [ + { + "hostWwnId": "CL1-A,0,21000024ff8f5296", + "portId": "CL1-A", + "hostGroupNumber": 0, + "hostGroupName": "1A-G00", + "hostWwn": "21000024ff8f5296", + "wwnNickname": "-" + } + ] +} +GET_HOST_ISCSI = { + "data": [ + { + "hostIscsiId": "CL1-A,0,iqn.ewdhehdhdhh", + "portId": "CL1-A", + "hostGroupNumber": 0, + "hostGroupName": "3C-G00", + "iscsiName": "iqn.ewdhehdhdhh", + "iscsiNickname": "test_tjy" + } + ] +} +GET_LUN_PATH = { + "data": [ + { + "lunId": "CL1-A,1,1", + "portId": "CL1-A", + "hostGroupNumber": 0, + "hostMode": "LINUX/IRIX", + "lun": 1, + "ldevId": 1 + } + ] +} +initator_result = [ + { + 'name': '21000024ff8f5296', + 'storage_id': '12345', + 'native_storage_host_initiator_id': '21000024ff8f5296', + 'wwn': '21000024ff8f5296', + 'status': 'online', + 'type': 'fc', + 'alias': 'CL1-A', + 'native_storage_host_id': 'CL1-A_0_21000024ff8f5296' + } +] +host_result = [ + { + 'name': 'test_tjy', + 'storage_id': '12345', + 'native_storage_host_id': 'CL1-A_0_iqn.ewdhehdhdhh', + 'os_type': 'Linux', + 'status': 'normal' + } +] +view_result = [ + { + 'name': 'CL1-A,1,1', + 'native_storage_host_group_id': 'CL1-A_0', + 'storage_id': '12345', + 'native_volume_id': '00:00:01', + 'native_masking_view_id': 'CL1-A_1_1' + } +] +groups_result = { + 'storage_host_groups': [ + { + 'name': '1A-G00', + 'storage_id': '12345', + 'native_storage_host_group_id': 'CL1-A_0', + 'storage_hosts': 'CL1-A_0_iqn.ewdhehdhdhh' + } + ], + 'storage_host_grp_host_rels': [ + { + 'storage_id': '12345', + 'native_storage_host_group_id': 'CL1-A_0', + 'native_storage_host_id': 'CL1-A_0_iqn.ewdhehdhdhh' + } + ] +} def create_driver(): @@ -551,3 +668,47 @@ def test_list_ports(self, mock_detail, mock_all): mock_detail.return_value = GET_DETAIL_PORT port = HitachiVspDriver(**ACCESS_INFO).list_ports(context) self.assertEqual(port, port_result) + + @mock.patch.object(RestHandler, 'get_specific_host_group') + @mock.patch.object(RestHandler, 'get_all_host_groups') + @mock.patch.object(RestHandler, 'get_host_wwn') + def test_host_initiators(self, mock_wwn, mock_groups, mock_group): + RestHandler.login = mock.Mock(return_value=None) + mock_groups.return_value = GET_ALL_GROUPS + mock_group.return_value = GET_SINGLE_WWN_GROUP + mock_wwn.return_value = GET_HOST_WWN + initiators = HitachiVspDriver( + **ACCESS_INFO).list_storage_host_initiators(context) + self.assertEqual(initiators, initator_result) + + @mock.patch.object(RestHandler, 'get_specific_host_group') + @mock.patch.object(RestHandler, 'get_all_host_groups') + @mock.patch.object(RestHandler, 'get_iscsi_name') + def test_hosts(self, mock_iscsi, mock_groups, mock_group): + RestHandler.login = mock.Mock(return_value=None) + mock_groups.return_value = GET_ALL_GROUPS + mock_group.return_value = GET_SINGLE_ISCSI_GROUP + mock_iscsi.return_value = GET_HOST_ISCSI + hosts = HitachiVspDriver(**ACCESS_INFO).list_storage_hosts(context) + self.assertEqual(hosts, host_result) + + @mock.patch.object(RestHandler, 'get_all_host_groups') + @mock.patch.object(RestHandler, 'get_lun_path') + def test_masking_views(self, mock_view, mock_groups): + RestHandler.login = mock.Mock(return_value=None) + mock_groups.return_value = GET_ALL_GROUPS + mock_view.return_value = GET_LUN_PATH + views = HitachiVspDriver(**ACCESS_INFO).list_masking_views(context) + self.assertEqual(views, view_result) + + @mock.patch.object(RestHandler, 'get_specific_host_group') + @mock.patch.object(RestHandler, 'get_all_host_groups') + @mock.patch.object(RestHandler, 'get_iscsi_name') + def test_host_groups(self, mock_iscsi, mock_groups, mock_group): + RestHandler.login = mock.Mock(return_value=None) + mock_groups.return_value = GET_ALL_GROUPS + mock_group.return_value = GET_SINGLE_ISCSI_GROUP + mock_iscsi.return_value = GET_HOST_ISCSI + groups = \ + HitachiVspDriver(**ACCESS_INFO).list_storage_host_groups(context) + self.assertEqual(groups, groups_result) From 09f3b1fd4687c95a65cb40386e2d8c4ad661f1e9 Mon Sep 17 00:00:00 2001 From: muniraj321 <76615492+muniraj321@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:49:39 +0530 Subject: [PATCH 10/17] update in this swagger.yaml update name and description in this file should be disk-> get/v1/disks --- openapi-spec/swagger.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openapi-spec/swagger.yaml b/openapi-spec/swagger.yaml index 538ac832e..879f493f5 100644 --- a/openapi-spec/swagger.yaml +++ b/openapi-spec/swagger.yaml @@ -1041,16 +1041,16 @@ paths: - abnormal responses: '200': - description: List port query was success + description: List disk query was success content: application/json: schema: type: object required: - - ports + - disks additionalProperties: true properties: - ports: + disks: type: array title: the disk schema items: From e53d3a164e008261ffc2d9c05dd340d1f886a10b Mon Sep 17 00:00:00 2001 From: yuanyu-ghca <79956159+yuanyu-ghca@users.noreply.github.com> Date: Fri, 6 May 2022 15:31:40 +0800 Subject: [PATCH 11/17] Emc vnx block add host mapping view (#807) --- .../vnx/vnx_block/component_handler.py | 122 ++++++++++++++++++ .../drivers/dell_emc/vnx/vnx_block/consts.py | 11 ++ .../dell_emc/vnx/vnx_block/navi_handler.py | 94 ++++++++++++++ .../dell_emc/vnx/vnx_block/vnx_block.py | 9 ++ .../dell_emc/vnx/vnx_block/test_vnx_block.py | 88 +++++++++++++ 5 files changed, 324 insertions(+) diff --git a/delfin/drivers/dell_emc/vnx/vnx_block/component_handler.py b/delfin/drivers/dell_emc/vnx/vnx_block/component_handler.py index 57310ec4c..914885c22 100644 --- a/delfin/drivers/dell_emc/vnx/vnx_block/component_handler.py +++ b/delfin/drivers/dell_emc/vnx/vnx_block/component_handler.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import copy import re import six @@ -443,3 +444,124 @@ def get_iscsi_ports(self): name = '%s-%s' % (iscsi_port.get('sp'), iscsi_port.get('port_id')) iscsi_port_map[name] = iscsi_port return iscsi_port_map + + def list_masking_views(self, storage_id): + views = self.navi_handler.list_masking_views() + views_list = [] + host_vv_set = set() + if views: + for view in views: + name = view.get('storage_group_name') + host_names = view.get('host_names') + lun_ids = view.get('lun_ids') + if name: + if name == '~physical' or name == '~management': + continue + view_model_template = { + 'native_masking_view_id': view.get( + 'storage_group_uid'), + "name": view.get('storage_group_name'), + "storage_id": storage_id + } + if host_names and lun_ids: + host_names = list(set(host_names)) + for host_name in host_names: + host_id = host_name.replace(' ', '') + for lun_id in lun_ids: + host_vv_key = '%s_%s' % (host_id, lun_id) + if host_vv_key in host_vv_set: + continue + host_vv_set.add(host_vv_key) + view_model = copy.deepcopy(view_model_template) + view_model[ + 'native_storage_host_id'] = host_id + view_model['native_volume_id'] = lun_id + view_model[ + 'native_masking_view_id'] = '%s_%s_%s' % ( + view_model.get('native_masking_view_id'), + host_id, lun_id) + views_list.append(view_model) + return views_list + + def list_storage_host_initiators(self, storage_id): + initiators = self.navi_handler.list_hbas() + initiators_list = [] + initiator_set = set() + port_types = {} + if initiators: + ports = self.list_ports(storage_id) + for port in (ports or []): + if port and port.get('type'): + port_types[port.get('name')] = port.get('type') + for initiator in (initiators or []): + if initiator and initiator.get('hba_uid'): + hba_uid = initiator.get('hba_uid') + type = '' + if port_types: + ports = initiator.get('port_ids') + if ports: + port_id = list(ports)[0] + type = port_types.get(port_id, '') + host_id = initiator.get('server_name', '').replace(' ', '') + if host_id == hba_uid: + host_id = None + if not host_id: + continue + if hba_uid in initiator_set: + continue + initiator_set.add(hba_uid) + + initiator_model = { + "name": hba_uid, + "storage_id": storage_id, + "native_storage_host_initiator_id": hba_uid, + "wwn": hba_uid, + "type": consts.INITIATOR_TYPE_MAP.get( + type.upper(), constants.InitiatorType.UNKNOWN), + "status": constants.InitiatorStatus.ONLINE, + "native_storage_host_id": host_id + } + initiators_list.append(initiator_model) + return initiators_list + + def list_storage_hosts(self, storage_id): + hosts = self.navi_handler.list_hbas() + host_list = [] + host_ids = set() + host_ips = {} + for host in (hosts or []): + if host and host.get('server_name'): + os_type = constants.HostOSTypes.UNKNOWN + os_name = host.get('hba_vendor_description') + ip_addr = host.get('server_ip_address') + if ip_addr == 'UNKNOWN': + continue + if os_name and 'VMware ESXi' in os_name: + os_type = constants.HostOSTypes.VMWARE_ESX + id = host.get('server_name').replace(' ', '') + if id in host_ids: + continue + host_ids.add(id) + + if ip_addr in host_ips.keys(): + first_port_ids = host_ips.get(ip_addr) + cur_port_ids = host.get('port_ids') + add_host = False + intersections = list( + set(first_port_ids).intersection(set(cur_port_ids))) + if not intersections: + add_host = True + if not add_host: + continue + host_ips[ip_addr] = host.get('port_ids') + + host_model = { + "name": host.get('server_name'), + "storage_id": storage_id, + "native_storage_host_id": id, + "os_type": os_type, + "status": constants.HostStatus.NORMAL, + "ip_address": ip_addr + } + host_list.append(host_model) + return host_list diff --git a/delfin/drivers/dell_emc/vnx/vnx_block/consts.py b/delfin/drivers/dell_emc/vnx/vnx_block/consts.py index 9348d8c4c..d012a74bb 100644 --- a/delfin/drivers/dell_emc/vnx/vnx_block/consts.py +++ b/delfin/drivers/dell_emc/vnx/vnx_block/consts.py @@ -64,6 +64,8 @@ GET_LOG_API = 'getlog -date %(begin_time)s %(end_time)s' EMCVNX_VENDOR = 'DELL EMC' RAID_GROUP_ID_PREFIX = 'raid_group_' +GET_SG_LIST_HOST_API = 'storagegroup -messner -list -host' +GET_PORT_LIST_HBA_API = 'port -list -hba' STATUS_MAP = { 'Ready': constants.StoragePoolStatus.NORMAL, 'Offline': constants.StoragePoolStatus.OFFLINE, @@ -143,3 +145,12 @@ 'SAS': constants.PortType.SAS, 'UNKNOWN': constants.PortType.OTHER } +INITIATOR_TYPE_MAP = { + 'FC': constants.InitiatorType.FC, + 'FCOE': constants.InitiatorType.FC, + 'ISCSI': constants.InitiatorType.ISCSI, + 'SAS': constants.InitiatorType.SAS, + 'UNKNOWN': constants.InitiatorType.UNKNOWN +} +ALU_PAIRS_PATTERN = '^[0-9]+\\s+[0-9]+$' +HBA_UID_PATTERN = "^\\s*HBA UID\\s+SP Name\\s+SPPort" diff --git a/delfin/drivers/dell_emc/vnx/vnx_block/navi_handler.py b/delfin/drivers/dell_emc/vnx/vnx_block/navi_handler.py index 767815976..7483ec955 100644 --- a/delfin/drivers/dell_emc/vnx/vnx_block/navi_handler.py +++ b/delfin/drivers/dell_emc/vnx/vnx_block/navi_handler.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import re import threading import six @@ -606,3 +607,96 @@ def navi_exe(self, command_str, host_ip=None): raise e finally: self.session_lock.release() + + def list_masking_views(self): + return self.get_resources_info(consts.GET_SG_LIST_HOST_API, + self.cli_sg_to_list) + + def cli_sg_to_list(self, resource_info): + obj_list = [] + obj_model = {} + try: + obj_infos = resource_info.split('\n') + pattern = re.compile(consts.ALU_PAIRS_PATTERN) + for obj_info in obj_infos: + str_line = obj_info.strip() + if str_line: + if ':' not in str_line: + search_obj = pattern.search(str_line) + if search_obj: + str_info = str_line.split() + lun_ids = obj_model.get('lun_ids') + if lun_ids: + lun_ids.add(str_info[1]) + else: + lun_ids = set() + lun_ids.add(str_info[1]) + obj_model['lun_ids'] = lun_ids + else: + str_info = self.split_str_by_colon(str_line) + if 'Host name:' in str_line: + host_names = obj_model.get('host_names') + if host_names: + host_names.add(str_info[1]) + else: + host_names = set() + host_names.add(str_info[1]) + obj_model['host_names'] = host_names + continue + + obj_model = self.str_info_to_model(str_info, obj_model) + + if str_line.startswith('Shareable:'): + obj_list = self.add_model_to_list(obj_model, + obj_list) + obj_model = {} + except Exception as e: + err_msg = "arrange sg info error: %s", six.text_type(e) + LOG.error(err_msg) + raise exception.InvalidResults(err_msg) + return obj_list + + def list_hbas(self): + return self.get_resources_info(consts.GET_PORT_LIST_HBA_API, + self.cli_hba_to_list) + + def cli_hba_to_list(self, resource_info): + obj_list = [] + obj_model = {} + sp_name = '' + port_ids = set() + try: + obj_infos = resource_info.split('\n') + for obj_info in obj_infos: + str_line = obj_info.strip() + if str_line: + if 'Information about each HBA:' in obj_info: + if obj_model: + obj_model['port_ids'] = port_ids + obj_list = self.add_model_to_list(obj_model, + obj_list) + obj_model = {} + port_ids = set() + sp_name = '' + if ':' in obj_info: + str_info = self.split_str_by_colon(str_line) + obj_model = self.str_info_to_model(str_info, obj_model) + if 'SP Name:' in obj_info: + sp_name = obj_info.replace('SP Name:', '').replace( + 'SP', '').replace('\r', '').replace(' ', '') + if 'SP Port ID:' in obj_info: + port_id = obj_info.replace('SP Port ID:', + '').replace('\r', + '').replace( + ' ', '') + port_id = '%s-%s' % (sp_name, port_id) + port_ids.add(port_id) + + if obj_model: + obj_model['port_ids'] = port_ids + obj_list.append(obj_model) + except Exception as e: + err_msg = "arrange host info error: %s", six.text_type(e) + LOG.error(err_msg) + raise exception.InvalidResults(err_msg) + return obj_list diff --git a/delfin/drivers/dell_emc/vnx/vnx_block/vnx_block.py b/delfin/drivers/dell_emc/vnx/vnx_block/vnx_block.py index bd836b262..467094208 100644 --- a/delfin/drivers/dell_emc/vnx/vnx_block/vnx_block.py +++ b/delfin/drivers/dell_emc/vnx/vnx_block/vnx_block.py @@ -77,3 +77,12 @@ def clear_alert(self, context, sequence_number): @staticmethod def get_access_url(): return 'https://{ip}' + + def list_storage_host_initiators(self, context): + return self.com_handler.list_storage_host_initiators(self.storage_id) + + def list_storage_hosts(self, context): + return self.com_handler.list_storage_hosts(self.storage_id) + + def list_masking_views(self, context): + return self.com_handler.list_masking_views(self.storage_id) diff --git a/delfin/tests/unit/drivers/dell_emc/vnx/vnx_block/test_vnx_block.py b/delfin/tests/unit/drivers/dell_emc/vnx/vnx_block/test_vnx_block.py index 00309f476..0f137da28 100644 --- a/delfin/tests/unit/drivers/dell_emc/vnx/vnx_block/test_vnx_block.py +++ b/delfin/tests/unit/drivers/dell_emc/vnx/vnx_block/test_vnx_block.py @@ -354,6 +354,46 @@ I/O Module Type : SAS """ +VIEW_DATAS = """ +Storage Group Name: AIX_PowerHA_node2 +Storage Group UID: 0B:33:4A:6E:81:38:EC:11:90:2B:00:60:16:63 +HBA/SP Pairs: + + HBA UID SP Name SPPort + ------- ------- ------ + 20:00:00:00:C9:76:5E:79:10:00:00:00:C9:76:5E:79 SP A 6 +Host name: AIX_21 + 20:00:00:00:C9:75:80:4C:10:00:00:00:C9:75:80:4C SP B 3 +Host name: AIX_21 + +HLU/ALU Pairs: + + HLU Number ALU Number + ---------- ---------- + 1 335 +Shareable: YES +""" +HBA_DATAS = """ +Information about each HBA: + +HBA UID: 20:00:00:00:C9:9B:57:79:10:00:00:00:C9:9B:57:79 +Server Name: aix_ma +Server IP Address: 8.44.129.26 +HBA Model Description: +HBA Vendor Description: +HBA Device Driver Name: N/A +Information about each port of this HBA: + + SP Name: SP A + SP Port ID: 6 + HBA Devicename: N/A + Trusted: NO + Logged In: NO + Defined: YES + Initiator Type: 3 + StorageGroup Name: None +""" + AGENT_RESULT = { 'agent_rev': '7.33.1 (0.38)', 'name': 'K10', @@ -520,6 +560,35 @@ 'ipv6': None, 'ipv6_mask': None }] +VIEW_RESULT = [ + { + 'native_masking_view_id': '0B:33:4A:6E:81:38:EC:11:90:2B:00:' + '60:16:63_AIX_21_335', + 'name': 'AIX_PowerHA_node2', + 'storage_id': '12345', + 'native_storage_host_id': 'AIX_21', + 'native_volume_id': '335' + }] +INITIATOR_RESULT = [ + { + 'name': '20:00:00:00:C9:9B:57:79:10:00:00:00:C9:9B:57:79', + 'storage_id': '12345', + 'native_storage_host_initiator_id': '20:00:00:00:C9:9B:57:79:10:' + '00:00:00:C9:9B:57:79', + 'wwn': '20:00:00:00:C9:9B:57:79:10:00:00:00:C9:9B:57:79', + 'type': 'fc', + 'status': 'online', + 'native_storage_host_id': 'aix_ma' + }] +HOST_RESULT = [ + { + 'name': 'aix_ma', + 'storage_id': '12345', + 'native_storage_host_id': 'aix_ma', + 'os_type': 'Unknown', + 'status': 'normal', + 'ip_address': '8.44.129.26' + }] def create_driver(): @@ -696,3 +765,22 @@ def test_get_ports(self): BUS_PORT_DATAS, BUS_PORT_STATE_DATAS]) ports = self.driver.list_ports(context) self.assertDictEqual(ports[0], PORT_RESULT[0]) + + def test_get_masking_views(self): + NaviClient.exec = mock.Mock(side_effect=[VIEW_DATAS]) + views = self.driver.list_masking_views(context) + self.assertDictEqual(views[0], VIEW_RESULT[0]) + + def test_get_initiators(self): + NaviClient.exec = mock.Mock(side_effect=[HBA_DATAS, + IO_PORT_CONFIG_DATAS, + ISCSI_PORT_DATAS, PORT_DATAS, + BUS_PORT_DATAS, + BUS_PORT_STATE_DATAS]) + initiators = self.driver.list_storage_host_initiators(context) + self.assertDictEqual(initiators[0], INITIATOR_RESULT[0]) + + def test_get_hosts(self): + NaviClient.exec = mock.Mock(side_effect=[HBA_DATAS]) + hosts = self.driver.list_storage_hosts(context) + self.assertDictEqual(hosts[0], HOST_RESULT[0]) From 21d151cfc6556ab41527cd4f9bd823d9e250885a Mon Sep 17 00:00:00 2001 From: tanjiangyu-ghca <79631193+tanjiangyu-ghca@users.noreply.github.com> Date: Fri, 6 May 2022 15:45:09 +0800 Subject: [PATCH 12/17] svc add host mapping view (#801) --- .../drivers/ibm/storwize_svc/ssh_handler.py | 122 ++++++++++++++++ .../drivers/ibm/storwize_svc/storwize_svc.py | 9 ++ .../ibm/storwize_svc/test_ibm_storwize_svc.py | 131 ++++++++++++++++++ 3 files changed, 262 insertions(+) diff --git a/delfin/drivers/ibm/storwize_svc/ssh_handler.py b/delfin/drivers/ibm/storwize_svc/ssh_handler.py index f8fed1c9d..51db58389 100644 --- a/delfin/drivers/ibm/storwize_svc/ssh_handler.py +++ b/delfin/drivers/ibm/storwize_svc/ssh_handler.py @@ -120,6 +120,21 @@ class SSHHandler(object): ALERT_NOT_FOUND_CODE = 'CMMVC8275E' BLOCK_SIZE = 512 BYTES_TO_BIT = 8 + OS_TYPE_MAP = {'generic': constants.HostOSTypes.UNKNOWN, + 'hpux': constants.HostOSTypes.HP_UX, + 'openvms': constants.HostOSTypes.OPEN_VMS, + 'tpgs': constants.HostOSTypes.UNKNOWN, + 'vvol': constants.HostOSTypes.UNKNOWN + } + INITIATOR_STATUS_MAP = {'active': constants.InitiatorStatus.ONLINE, + 'offline': constants.InitiatorStatus.OFFLINE, + 'inactive': constants.InitiatorStatus.ONLINE + } + HOST_STATUS_MAP = {'online': constants.HostStatus.NORMAL, + 'offline': constants.HostStatus.OFFLINE, + 'degraded': constants.HostStatus.DEGRADED, + 'mask': constants.HostStatus.NORMAL, + } def __init__(self, **kwargs): self.ssh_pool = SSHPool(**kwargs) @@ -1044,3 +1059,110 @@ def get_latest_perf_timestamp(self): if latest_time < occur_time: latest_time = occur_time return latest_time + + def list_storage_hosts(self, storage_id): + try: + host_list = [] + hosts = self.exec_ssh_command('lshost') + host_res = hosts.split('\n') + for i in range(1, len(host_res)): + if host_res[i] is None or host_res[i] == '': + continue + control_str = ' '.join(host_res[i].split()) + str_info = control_str.split(' ') + host_id = str_info[0] + detail_command = 'lshost %s' % host_id + deltail_info = self.exec_ssh_command(detail_command) + host_map = {} + self.handle_detail(deltail_info, host_map, split=' ') + status = SSHHandler.HOST_STATUS_MAP.get(host_map.get('status')) + host_result = { + "name": host_map.get('name'), + "storage_id": storage_id, + "native_storage_host_id": host_map.get('id'), + "os_type": SSHHandler.OS_TYPE_MAP.get( + host_map.get('type', '').lower()), + "status": status + } + host_list.append(host_result) + return host_list + except Exception as e: + LOG.error("Failed to get host metrics from svc") + raise e + + def list_masking_views(self, storage_id): + try: + view_list = [] + hosts = self.exec_ssh_command('lshostvdiskmap') + host_res = hosts.split('\n') + for i in range(1, len(host_res)): + if host_res[i] is None or host_res[i] == '': + continue + control_str = ' '.join(host_res[i].split()) + str_info = control_str.split(' ') + if len(str_info) > 3: + host_id = str_info[0] + vdisk_id = str_info[3] + view_id = '%s_%s' % (str_info[0], str_info[3]) + view_result = { + "name": view_id, + "native_storage_host_id": host_id, + "storage_id": storage_id, + "native_volume_id": vdisk_id, + "native_masking_view_id": view_id, + } + view_list.append(view_result) + return view_list + except Exception as e: + LOG.error("Failed to get view metrics from svc") + raise e + + def list_storage_host_initiators(self, storage_id): + try: + initiator_list = [] + hosts = self.exec_ssh_command('lshost') + host_res = hosts.split('\n') + for i in range(1, len(host_res)): + if host_res[i] is None or host_res[i] == '': + continue + control_str = ' '.join(host_res[i].split()) + str_info = control_str.split(' ') + host_id = str_info[0] + detail_command = 'lshost %s' % host_id + deltail_info = self.exec_ssh_command(detail_command) + init_name = None + type = None + host_id = None + for host in deltail_info.split('\n'): + if host: + strinfo = host.split(' ', 1) + key = strinfo[0] + value = None + if len(strinfo) > 1: + value = strinfo[1] + if key == 'WWPN': + init_name = value + type = 'fc' + elif key == 'iscsi_name': + init_name = value + type = 'iscsi' + elif key == 'id': + host_id = value + elif key == 'state' and init_name: + status = SSHHandler.INITIATOR_STATUS_MAP.get(value) + init_result = { + "name": init_name, + "storage_id": storage_id, + "native_storage_host_initiator_id": init_name, + "wwn": init_name, + "status": status, + "type": type, + "native_storage_host_id": host_id + } + initiator_list.append(init_result) + init_name = None + type = None + return initiator_list + except Exception as e: + LOG.error("Failed to get initiators metrics from svc") + raise e diff --git a/delfin/drivers/ibm/storwize_svc/storwize_svc.py b/delfin/drivers/ibm/storwize_svc/storwize_svc.py index 571d337b6..701d7bd78 100644 --- a/delfin/drivers/ibm/storwize_svc/storwize_svc.py +++ b/delfin/drivers/ibm/storwize_svc/storwize_svc.py @@ -86,3 +86,12 @@ def get_capabilities(context, filters=None): def get_latest_perf_timestamp(self, context): return self.ssh_hanlder.get_latest_perf_timestamp() + + def list_storage_hosts(self, context): + return self.ssh_hanlder.list_storage_hosts(self.storage_id) + + def list_masking_views(self, context): + return self.ssh_hanlder.list_masking_views(self.storage_id) + + def list_storage_host_initiators(self, context): + return self.ssh_hanlder.list_storage_host_initiators(self.storage_id) diff --git a/delfin/tests/unit/drivers/ibm/storwize_svc/test_ibm_storwize_svc.py b/delfin/tests/unit/drivers/ibm/storwize_svc/test_ibm_storwize_svc.py index ea62646c0..cd1151e21 100644 --- a/delfin/tests/unit/drivers/ibm/storwize_svc/test_ibm_storwize_svc.py +++ b/delfin/tests/unit/drivers/ibm/storwize_svc/test_ibm_storwize_svc.py @@ -1366,6 +1366,113 @@ def __init__(self): }, values={ 1638346330000: 0.0 })] +get_all_hosts = """id name +1 host1 +""" +get_host_summery = """id 38 +name tjy_test_iscsi +port_count 3 +type generic +mask 11111111111111111111111111111111111111 +iogrp_count 4 +status online +site_id +site_name +host_cluster_id +host_cluster_name +WWPN 21000024FF543B0C +node_logged_in_count 1 +state inactive +WWPN 21000024FF438098 +node_logged_in_count 1 +state active +WWPN 21000024FF41C461 +node_logged_in_count 1 +state inactive +""" +host_result = [ + { + 'name': 'tjy_test_iscsi', + 'storage_id': '12345', + 'native_storage_host_id': '38', + 'os_type': 'Unknown', + 'status': 'normal' + } +] +get_all_views = """id name SCSI_id vdisk_id vdisk_name +2 Solaris11.3_57 0 27 PG_1 +6 hwstorage_8.44.133.80 0 24 wyktest +7 VNX-WIN8-TEST 0 31 SVC-WIN8_test +14 pd_esx6 0 65 pd_taiping0 +14 pd_esx6 1 66 pd_taiping1 +14 pd_esx6 2 67 pd_taiping2 +""" +view_result = [ + { + 'name': '2_27', + 'native_storage_host_id': '2', + 'storage_id': '12345', + 'native_volume_id': '27', + 'native_masking_view_id': '2_27' + }, { + 'name': '6_24', + 'native_storage_host_id': '6', + 'storage_id': '12345', + 'native_volume_id': '24', + 'native_masking_view_id': '6_24' + }, { + 'name': '7_31', + 'native_storage_host_id': '7', + 'storage_id': '12345', + 'native_volume_id': '31', + 'native_masking_view_id': '7_31' + }, { + 'name': '14_65', + 'native_storage_host_id': '14', + 'storage_id': '12345', + 'native_volume_id': '65', + 'native_masking_view_id': '14_65' + }, { + 'name': '14_66', + 'native_storage_host_id': '14', + 'storage_id': '12345', + 'native_volume_id': '66', + 'native_masking_view_id': '14_66' + }, { + 'name': '14_67', + 'native_storage_host_id': '14', + 'storage_id': '12345', + 'native_volume_id': '67', + 'native_masking_view_id': '14_67' + } +] +init_result = [ + { + 'name': '21000024FF543B0C', + 'storage_id': '12345', + 'native_storage_host_initiator_id': '21000024FF543B0C', + 'wwn': '21000024FF543B0C', + 'status': 'online', + 'type': 'fc', + 'native_storage_host_id': '38' + }, { + 'name': '21000024FF438098', + 'storage_id': '12345', + 'native_storage_host_initiator_id': '21000024FF438098', + 'wwn': '21000024FF438098', + 'status': 'online', + 'type': 'fc', + 'native_storage_host_id': '38' + }, { + 'name': '21000024FF41C461', + 'storage_id': '12345', + 'native_storage_host_initiator_id': '21000024FF41C461', + 'wwn': '21000024FF41C461', + 'status': 'online', + 'type': 'fc', + 'native_storage_host_id': '38' + } +] def create_driver(): @@ -1492,3 +1599,27 @@ def test_collect_perf_metrics(self, mock_ssh_get, mock_file_list, resource_metrics, start_time, end_time) self.assertEqual(metrics[0][1]['resource_name'], 'powerha') + + @mock.patch.object(SSHHandler, 'do_exec') + @mock.patch.object(SSHPool, 'get') + def test_list_hosts(self, mock_ssh_get, mock_host): + mock_ssh_get.return_value = {paramiko.SSHClient()} + mock_host.side_effect = [get_all_hosts, get_host_summery] + host = self.driver.list_storage_hosts(context) + self.assertEqual(host, host_result) + + @mock.patch.object(SSHHandler, 'do_exec') + @mock.patch.object(SSHPool, 'get') + def test_masking_views(self, mock_ssh_get, mock_view): + mock_ssh_get.return_value = {paramiko.SSHClient()} + mock_view.return_value = get_all_views + view = self.driver.list_masking_views(context) + self.assertEqual(view, view_result) + + @mock.patch.object(SSHHandler, 'do_exec') + @mock.patch.object(SSHPool, 'get') + def test_list_host_initiators(self, mock_ssh_get, mock_host): + mock_ssh_get.return_value = {paramiko.SSHClient()} + mock_host.side_effect = [get_all_hosts, get_host_summery] + init = self.driver.list_storage_host_initiators(context) + self.assertEqual(init, init_result) From 3d0c190c02c00fc5587cecb5c1467deedfb3c7e9 Mon Sep 17 00:00:00 2001 From: yuanyu-ghca <79956159+yuanyu-ghca@users.noreply.github.com> Date: Fri, 6 May 2022 15:59:03 +0800 Subject: [PATCH 13/17] Hpe 3par add host mapping view (#798) --- .../drivers/hpe/hpe_3par/component_handler.py | 252 ++++ delfin/drivers/hpe/hpe_3par/consts.py | 38 +- delfin/drivers/hpe/hpe_3par/hpe_3parstor.py | 21 +- delfin/drivers/hpe/hpe_3par/rest_handler.py | 7 + delfin/drivers/hpe/hpe_3par/ssh_handler.py | 106 ++ .../drivers/hpe/hpe_3par/test_hpe_3parstor.py | 1185 ++++++++++++++++- 6 files changed, 1601 insertions(+), 8 deletions(-) diff --git a/delfin/drivers/hpe/hpe_3par/component_handler.py b/delfin/drivers/hpe/hpe_3par/component_handler.py index 9c7e86ea1..5f3dfc4f3 100644 --- a/delfin/drivers/hpe/hpe_3par/component_handler.py +++ b/delfin/drivers/hpe/hpe_3par/component_handler.py @@ -417,3 +417,255 @@ def parse_speed(self, speed_value): err_msg = "analyse speed error: %s" % (six.text_type(err)) LOG.error(err_msg) return speed + + def list_storage_host_initiators(self, storage_id): + initiators = self.ssh_handler.list_storage_host_initiators() + initiators_list = [] + wwn_set = set() + for initiator in (initiators or []): + if initiator: + wwn = initiator.get('wwn/iscsi_name', '').replace('-', '') + if wwn: + if wwn in wwn_set: + continue + wwn_set.add(wwn) + ip_addr = initiator.get('ip_addr') + type = constants.InitiatorType.FC + if ip_addr and ip_addr != 'n/a': + type = constants.InitiatorType.ISCSI + initiator_model = { + "name": wwn, + "storage_id": storage_id, + "native_storage_host_initiator_id": wwn, + "wwn": wwn, + "type": type, + "status": constants.InitiatorStatus.ONLINE, + "native_storage_host_id": initiator.get('id', + '').replace( + '-', ''), + } + initiators_list.append(initiator_model) + return initiators_list + + def list_storage_hosts(self, storage_id): + host_datas = self.rest_handler.list_storage_host() + host_list = [] + if host_datas: + hosts = host_datas.get('members') + for host in (hosts or []): + if host and host.get('name'): + descriptors = host.get('descriptors') + comment = None + os = '' + ip_addr = None + if descriptors: + comment = descriptors.get('comment') + os = descriptors.get('os', '') + ip_addr = descriptors.get('IPAddr') + host_model = { + "name": host.get('name'), + "description": comment, + "storage_id": storage_id, + "native_storage_host_id": host.get('id'), + "os_type": consts.HOST_OS_MAP.get( + os, constants.HostOSTypes.UNKNOWN), + "status": constants.HostStatus.NORMAL, + "ip_address": ip_addr + } + host_list.append(host_model) + return host_list + + def list_storage_host_groups(self, storage_id): + host_groups = self.ssh_handler.list_storage_host_groups() + host_group_list = [] + result = {} + if host_groups: + hosts_map = self.ssh_handler.get_resources_ids( + self.ssh_handler.HPE3PAR_COMMAND_SHOWHOST_D, + consts.HOST_OR_VV_PATTERN) + for host_group in host_groups: + host_members = host_group.get('members') + host_ids = [] + if hosts_map: + for host_name in (host_members or []): + host_id = hosts_map.get(host_name) + if host_id: + host_ids.append(host_id) + host_group_model = { + "name": host_group.get('name'), + "description": host_group.get('comment'), + "storage_id": storage_id, + "native_storage_host_group_id": host_group.get('id'), + "storage_hosts": ','.join(host_ids) + } + host_group_list.append(host_group_model) + storage_host_grp_relation_list = [] + for storage_host_group in host_group_list: + storage_hosts = storage_host_group.pop('storage_hosts', None) + if not storage_hosts: + continue + storage_hosts = storage_hosts.split(',') + + for storage_host in storage_hosts: + storage_host_group_relation = { + 'storage_id': storage_id, + 'native_storage_host_group_id': storage_host_group.get( + 'native_storage_host_group_id'), + 'native_storage_host_id': storage_host + } + storage_host_grp_relation_list \ + .append(storage_host_group_relation) + + result = { + 'storage_host_groups': host_group_list, + 'storage_host_grp_host_rels': storage_host_grp_relation_list + } + return result + + def list_port_groups(self, storage_id): + views = self.ssh_handler.list_masking_views() + port_groups_list = [] + port_list = [] + for view in (views or []): + port = view.get('port', '').replace('-', '') + if port: + if port in port_list: + continue + port_list.append(port) + port_group_model = { + "name": "port_group_" + port, + "description": "port_group_" + port, + "storage_id": storage_id, + "native_port_group_id": "port_group_" + port, + "ports": port + } + port_groups_list.append(port_group_model) + port_group_relation_list = [] + for port_group in port_groups_list: + ports = port_group.pop('ports', None) + if not ports: + continue + ports = ports.split(',') + + for port in ports: + port_group_relation = { + 'storage_id': storage_id, + 'native_port_group_id': + port_group.get('native_port_group_id'), + 'native_port_id': port + } + port_group_relation_list.append(port_group_relation) + result = { + 'port_groups': port_groups_list, + 'port_grp_port_rels': port_group_relation_list + } + return result + + def list_volume_groups(self, storage_id): + volume_groups = self.ssh_handler.list_volume_groups() + volume_group_list = [] + result = {} + if volume_groups: + volumes_map = self.ssh_handler.get_resources_ids( + self.ssh_handler.HPE3PAR_COMMAND_SHOWVV, + consts.HOST_OR_VV_PATTERN) + for volume_group in volume_groups: + volume_members = volume_group.get('members') + volume_ids = [] + if volumes_map: + for volume_name in (volume_members or []): + volume_id = volumes_map.get(volume_name) + if volume_id: + volume_ids.append(volume_id) + volume_group_model = { + "name": volume_group.get('name'), + "description": volume_group.get('comment'), + "storage_id": storage_id, + "native_volume_group_id": volume_group.get('id'), + "volumes": ','.join(volume_ids) + } + volume_group_list.append(volume_group_model) + volume_group_relation_list = [] + for volume_group in volume_group_list: + volumes = volume_group.pop('volumes', None) + if not volumes: + continue + volumes = volumes.split(',') + + for volume in volumes: + volume_group_relation = { + 'storage_id': storage_id, + 'native_volume_group_id': + volume_group.get('native_volume_group_id'), + 'native_volume_id': volume} + volume_group_relation_list.append(volume_group_relation) + + result = { + 'volume_groups': volume_group_list, + 'vol_grp_vol_rels': volume_group_relation_list + } + return result + + def list_masking_views(self, storage_id): + views = self.ssh_handler.list_masking_views() + views_list = [] + if views: + hosts_map = self.ssh_handler.get_resources_ids( + self.ssh_handler.HPE3PAR_COMMAND_SHOWHOST_D, + consts.HOST_OR_VV_PATTERN) + hosts_group_map = self.ssh_handler.get_resources_ids( + self.ssh_handler.HPE3PAR_COMMAND_SHOWHOSTSET_D, + consts.HOST_OR_VV_PATTERN) + volumes_map = self.ssh_handler.get_resources_ids( + self.ssh_handler.HPE3PAR_COMMAND_SHOWVV, + consts.HOST_OR_VV_PATTERN) + volumes_group_map = self.ssh_handler.get_resources_ids( + self.ssh_handler.HPE3PAR_COMMAND_SHOWVVSET_D, + consts.HOST_OR_VV_PATTERN) + host_vv_set = set() + for view in views: + vv_name = view.get('vvname') + host_name = view.get('hostname') + if vv_name and host_name: + host_vv_key = '%s_%s' % (host_name, vv_name) + host_vv_key = host_vv_key.replace(' ', '') + if host_vv_key in host_vv_set: + continue + host_vv_set.add(host_vv_key) + port = view.get('port', '').replace('-', '') + lun_id = view.get('lun') + wwn = view.get('host_wwn/iscsi_name', '').replace('-', '') + native_port_group_id = None + if port: + lun_id = '%s_%s' % (lun_id, port) + native_port_group_id = 'port_group_%s' % port + if wwn: + lun_id = '%s_%s' % (lun_id, wwn) + lun_id = '%s_%s' % (lun_id, host_vv_key) + view_model = { + 'native_masking_view_id': lun_id, + "name": view.get('lun'), + 'native_port_group_id': native_port_group_id, + "storage_id": storage_id + } + if 'set:' in vv_name: + vv_set_id = volumes_group_map.get( + vv_name.replace('set:', '')) + view_model['native_volume_group_id'] = vv_set_id + else: + vv_id = volumes_map.get(vv_name) + view_model['native_volume_id'] = vv_id + if 'set:' in host_name: + host_set_id = hosts_group_map.get( + host_name.replace('set:', '')) + view_model[ + 'native_storage_host_group_id'] = host_set_id + else: + host_id = hosts_map.get(host_name) + view_model['native_storage_host_id'] = host_id + if (view_model.get('native_storage_host_id') + or view_model.get('native_storage_host_group_id')) \ + and (view_model.get('native_volume_id') + or view_model.get('native_volume_group_id')): + views_list.append(view_model) + return views_list diff --git a/delfin/drivers/hpe/hpe_3par/consts.py b/delfin/drivers/hpe/hpe_3par/consts.py index 57d61c8db..895573929 100644 --- a/delfin/drivers/hpe/hpe_3par/consts.py +++ b/delfin/drivers/hpe/hpe_3par/consts.py @@ -655,6 +655,11 @@ "ShareDir\\s+State" VFS_PATTERN = "^\\s*VFS\\s+FPG\\s+IPAddr\\s+State" IPV4_PATTERN = "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" + +HOST_OR_VV_SET_PATTERN = "^\\s*Id\\s+Name\\s+Members\\s+Comment" +HOST_OR_VV_PATTERN = "^\\s*Id\\s+Name\\s+" +VLUN_PATTERN = "^\\s*Lun\\s+VVName\\s+HostName" + CONTROLLER_STATUS_MAP = { 'OK': constants.ControllerStatus.NORMAL, 'NORMAL': constants.ControllerStatus.NORMAL, @@ -668,7 +673,7 @@ } DISK_STATUS_MAP = { 'NORMAL': constants.DiskStatus.NORMAL, - 'DEGRADED': constants.DiskStatus.ABNORMAL, + 'DEGRADED': constants.DiskStatus.DEGRADED, 'FAILED': constants.DiskStatus.ABNORMAL, 'NEW': constants.DiskStatus.ABNORMAL } @@ -705,3 +710,34 @@ 1: "control", 2: "data" } +HOST_OS_MAP = { + 'AIX': constants.HostOSTypes.AIX, + 'Citrix Xen Server 5.x/6.x': constants.HostOSTypes.XEN_SERVER, + 'Citrix Xen Server 7.x': constants.HostOSTypes.XEN_SERVER, + 'HP-UX': constants.HostOSTypes.HP_UX, + 'HP-UX (11i v1,11i v2)': constants.HostOSTypes.HP_UX, + 'HP-UX (11i v3)': constants.HostOSTypes.HP_UX, + 'OpenVMS': constants.HostOSTypes.OPEN_VMS, + 'Oracle VM x86': constants.HostOSTypes.ORACLE_VM, + 'Solaris 11': constants.HostOSTypes.SOLARIS, + 'Solaris 9/10': constants.HostOSTypes.SOLARIS, + 'VMware (ESXi)': constants.HostOSTypes.VMWARE_ESX, + 'ESXI6.0': constants.HostOSTypes.VMWARE_ESX, + 'ESX 4.x/5.x': constants.HostOSTypes.VMWARE_ESX, + 'Windows 2003': constants.HostOSTypes.WINDOWS, + 'Windows 2008/2008 R2': constants.HostOSTypes.WINDOWS, + 'Windows 2012': constants.HostOSTypes.WINDOWS_SERVER_2012, + 'Windows 2012 / WS2012 R2': constants.HostOSTypes.WINDOWS_SERVER_2012, + 'Windows Server 2016': constants.HostOSTypes.WINDOWS, + 'Red Hat Enterprise Linux': constants.HostOSTypes.LINUX, + 'OE Linux UEK (5.x, 6.x)': constants.HostOSTypes.LINUX, + 'OE Linux UEK 7.x': constants.HostOSTypes.LINUX, + 'RHE Linux (5.x, 6.x)': constants.HostOSTypes.LINUX, + 'RHE Linux (Pre RHEL 5)': constants.HostOSTypes.LINUX, + 'RHE Linux 7.x': constants.HostOSTypes.LINUX, + 'SuSE (10.x, 11.x)': constants.HostOSTypes.LINUX, + 'SuSE': constants.HostOSTypes.LINUX, + 'SuSE 12.x': constants.HostOSTypes.LINUX, + 'SuSE Linux (Pre SLES 10)': constants.HostOSTypes.LINUX, + 'SuSE Virtualization': constants.HostOSTypes.LINUX +} diff --git a/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py b/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py index c655ccefb..e425046ed 100644 --- a/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py +++ b/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py @@ -96,11 +96,20 @@ def parse_alert(context, alert): def clear_alert(self, context, alert): return self.alert_handler.clear_alert(context, alert) - def list_filesystems(self, context): - pass + def list_storage_host_initiators(self, context): + return self.comhandler.list_storage_host_initiators(self.storage_id) - def list_qtrees(self, context): - pass + def list_storage_hosts(self, context): + return self.comhandler.list_storage_hosts(self.storage_id) - def list_shares(self, context): - pass + def list_storage_host_groups(self, context): + return self.comhandler.list_storage_host_groups(self.storage_id) + + def list_port_groups(self, context): + return self.comhandler.list_port_groups(self.storage_id) + + def list_volume_groups(self, context): + return self.comhandler.list_volume_groups(self.storage_id) + + def list_masking_views(self, context): + return self.comhandler.list_masking_views(self.storage_id) diff --git a/delfin/drivers/hpe/hpe_3par/rest_handler.py b/delfin/drivers/hpe/hpe_3par/rest_handler.py index af135bfad..0fde1bf84 100644 --- a/delfin/drivers/hpe/hpe_3par/rest_handler.py +++ b/delfin/drivers/hpe/hpe_3par/rest_handler.py @@ -38,6 +38,8 @@ class RestHandler(object): REST_ALERTS_URL = '/api/v1/eventlog?query="category EQ 2"' + REST_HOSTS_URL = '/api/v1/hosts' + REST_AUTH_KEY = 'X-HP3PAR-WSAPI-SessionKey' session_lock = None @@ -196,3 +198,8 @@ def get_all_volumes(self): rejson = self.get_resinfo_call(RestHandler.REST_VOLUMES_URL, method='GET') return rejson + + def list_storage_host(self): + rejson = self.get_resinfo_call(RestHandler.REST_HOSTS_URL, + method='GET') + return rejson diff --git a/delfin/drivers/hpe/hpe_3par/ssh_handler.py b/delfin/drivers/hpe/hpe_3par/ssh_handler.py index f9155caac..095cffee2 100644 --- a/delfin/drivers/hpe/hpe_3par/ssh_handler.py +++ b/delfin/drivers/hpe/hpe_3par/ssh_handler.py @@ -48,6 +48,11 @@ class SSHHandler(object): HPE3PAR_COMMAND_SHOWPORT_RCIP = 'showport -rcip' HPE3PAR_COMMAND_SHOWPORT_FCOE = 'showport -fcoe' HPE3PAR_COMMAND_SHOWPORT_FS = 'showport -fs' + HPE3PAR_COMMAND_SHOWHOSTSET_D = 'showhostset -d' + HPE3PAR_COMMAND_SHOWVVSET_D = 'showvvset -d' + HPE3PAR_COMMAND_SHOWHOST_D = 'showhost -d' + HPE3PAR_COMMAND_SHOWVV = 'showvv' + HPE3PAR_COMMAND_SHOWVLUN_T = 'showvlun -t' def __init__(self, **kwargs): self.kwargs = kwargs @@ -288,6 +293,23 @@ def parse_datas_to_list(self, resource_info, pattern_str, para_map=None): str_info, obj_list, titles) + elif para_map and para_map.get('command', '') \ + == 'parse_set_groups_table': + if '---------------------------------' in str_line: + break + obj_list = self.parse_set_groups_table(cols_size, + titles_size, + str_info, + obj_list) + elif para_map and para_map.get('command', '') \ + == 'parse_view_table': + if '---------------------------------' in str_line: + break + obj_list = self.parse_view_table(cols_size, + titles_size, + str_info, + obj_list, + titles) else: if cols_size == titles_size: obj_model = {} @@ -414,3 +436,87 @@ def exec_command(self, command): LOG.error("command %s failed: %s" % (command, re)) raise exception.StorageBackendException(re) return re + + def list_storage_host_groups(self): + para_map = { + 'command': 'parse_set_groups_table' + } + return self.get_resources_info( + SSHHandler.HPE3PAR_COMMAND_SHOWHOSTSET_D, + self.parse_datas_to_list, + pattern_str=consts.HOST_OR_VV_SET_PATTERN, + para_map=para_map) + + def list_volume_groups(self): + para_map = { + 'command': 'parse_set_groups_table' + } + return self.get_resources_info( + SSHHandler.HPE3PAR_COMMAND_SHOWVVSET_D, + self.parse_datas_to_list, + pattern_str=consts.HOST_OR_VV_SET_PATTERN, + para_map=para_map) + + def parse_set_groups_table(self, cols_size, titles_size, str_info, + obj_list): + if cols_size >= titles_size: + members = [] + value = str_info[2].replace('-', '') + if value: + members = [str_info[2]] + obj_model = { + 'id': str_info[0], + 'name': str_info[1], + 'members': members, + 'comment': (" ".join(str_info[3:])).replace('-', ''), + } + obj_list.append(obj_model) + elif obj_list and cols_size == 1: + value = str_info[0].replace('-', '') + if value: + obj_model = obj_list[-1] + if obj_model and obj_model.get('members'): + obj_model.get('members').append(str_info[0]) + else: + members = [str_info[0]] + obj_model['members'] = members + + return obj_list + + def parse_view_table(self, cols_size, titles_size, str_info, obj_list, + titles): + if cols_size >= titles_size: + obj_model = {} + for i in range(titles_size): + key = titles[i].lower().replace('-', '') + obj_model[key] = str_info[i] + if obj_model: + obj_list.append(obj_model) + return obj_list + + def get_resources_ids(self, command, pattern_str, para_map=None): + if not para_map: + para_map = { + 'key_position': 1, + 'value_position': 0 + } + return self.get_resources_info(command, + self.parse_datas_to_map, + pattern_str=pattern_str, + para_map=para_map, throw_excep=False) + + def list_storage_host_initiators(self): + return self.get_resources_info( + SSHHandler.HPE3PAR_COMMAND_SHOWHOST_D, + self.parse_datas_to_list, + pattern_str=consts.HOST_OR_VV_PATTERN) + + def list_masking_views(self): + para_map = { + 'command': 'parse_view_table' + } + return self.get_resources_info( + SSHHandler.HPE3PAR_COMMAND_SHOWVLUN_T, + self.parse_datas_to_list, + pattern_str=consts.VLUN_PATTERN, + para_map=para_map) diff --git a/delfin/tests/unit/drivers/hpe/hpe_3par/test_hpe_3parstor.py b/delfin/tests/unit/drivers/hpe/hpe_3par/test_hpe_3parstor.py index e70629adb..700fc1462 100644 --- a/delfin/tests/unit/drivers/hpe/hpe_3par/test_hpe_3parstor.py +++ b/delfin/tests/unit/drivers/hpe/hpe_3par/test_hpe_3parstor.py @@ -146,6 +146,1095 @@ def __init__(self): 108 """ +HOST_GROUP_DATAS = """ + Id Name Members Comment +194 HostSet_VMware Host_ESXi6.5_125 -- +229 HostSet_Suse11_Oracle Host_Suse11_8.44.75.122 -- +257 HostGroup_ESX6.0 ESX6.0_8.44.75.145 -- + ESX6.0_8.44.75.146 +264 HostSet_Win2016_WSFC RH2288V5_Win2016_node2 -- + RH2288V5_Win2016_node1 +266 HostSet_Win2012_WSFC RH2285_Win2012_wsfc1 -- + Rh2285_Win2012_wsfc2 +268 HostSet_AIX Host_AIX_51.10.192.20 -- +270 HostSet_Suse11 Host_Suse11_8.44.75.123 -- +274 Suse11sp4_150 litng138.150 -- +----------------------------------------------------------- + 32 total 28 +""" +HOST_ID_DATAS = """ + Id Name Persona -WWN/iSCSI_Name- Port IP_addr + 175 Host_ESXi6.5_125 Generic 2408244427906812 --- n/a + 54 Doradov3_lm Generic 2418244427906812 --- n/a + 57 AIX_wenbin AIX-legacy 10000000C9E74BCC --- n/a + 65 SKY-ESXI60 Generic 2100001B321BE0FF --- n/a + 65 SKY-ESXI60 Generic 2101001B323BE0FF --- n/a + 67 zouming Generic 2012E4A8B6B0A1CC --- n/a + 67 zouming Generic 2002E4A8B6B0A1CC --- n/a + 68 powerpath Generic 21000024FF36D406 --- n/a + 68 powerpath Generic 21000024FF36D407 --- n/a + 69 power_v3 Generic 20809CE37435D845 --- n/a + 69 power_v3 Generic 20909CE37435D845 --- n/a + 89 vplex_meta_important Generic 5000144280292012 0:1:2 n/a + 89 vplex_meta_important Generic 5000144280292010 0:1:2 n/a + 89 vplex_meta_important Generic 5000144290292012 1:1:2 n/a + 89 vplex_meta_important Generic 500014429029E910 1:1:2 n/a + 89 vplex_meta_important Generic 500014429029E912 1:1:2 n/a + 89 vplex_meta_important Generic 500014428029E912 1:1:2 n/a + 89 vplex_meta_important Generic 500014428029E910 1:1:2 n/a + 89 vplex_meta_important Generic 5000144290292010 1:1:2 n/a + 89 vplex_meta_important Generic 5000144290292012 0:1:2 n/a + 89 vplex_meta_important Generic 5000144290292010 0:1:2 n/a + 89 vplex_meta_important Generic 500014429029E912 0:1:2 n/a + 89 vplex_meta_important Generic 500014429029E910 0:1:2 n/a + 89 vplex_meta_important Generic 5000144280292012 1:1:2 n/a + 89 vplex_meta_important Generic 5000144280292010 1:1:2 n/a + 89 vplex_meta_important Generic 500014428029E912 0:1:2 n/a + 89 vplex_meta_important Generic 500014428029E910 0:1:2 n/a + 91 Dorado5000_51.45 Generic 200080D4A58EA53A --- n/a + 91 Dorado5000_51.45 Generic 201080D4A58EA53A --- n/a + 98 AIX6.1_LN AIX-legacy 10000000C9781C57 --- n/a + 98 AIX6.1_LN AIX-legacy 10000000C9781853 --- n/a +115 huhuihost Generic 2100000E1E1A9B30 --- n/a +121 Dorado5000V3_F3 Generic 201880D4A58EA53A --- n/a +160 host002 Generic 21000024FF41DCF8 --- n/a + -- -- -- 21000024FF41DCF7 1:0:2 n/a + -- -- -- 21000024FF41DCF6 1:0:2 n/a + -- -- -- 21000024FF0CC6CA 0:1:2 n/a + -- -- -- 21000024FF0CC6CA 1:1:2 n/a + -- -- -- 21000024FF0CBF47 0:1:2 n/a + -- -- -- 21000024FF0CBF47 1:1:2 n/a +""" +VOLUME_GROUP_DATAS = """ +Id Name Members Comment + 91 wcj_2 wcj_2.0 -- + wcj_2.1 + wcj_2.2 + wcj_2.3 +110 HP-Esxi-LUNSet -- -- +124 zhangjun -- -- +126 wcj_1 wcj_1.1 -- +127 wcj_3 wcj_3.0 -- + wcj_3.1 +128 IBM_SVC -- -- +129 zyz_3parF200_ zyz_3parF200.0 -- + zyz_3parF200.1 + zyz_3parF200.2 + zyz_3parF200.3 +130 zyz zyz_2 -- +131 tx -- -- +132 tx9 -- -- +133 wcj_hp_1 -- -- +136 AIX_YG_WYK_LUN AIX_YG_WYK_LUN.0 -- + AIX_YG_WYK_LUN.1 + AIX_YG_WYK_LUN.2 + AIX_YG_WYK_LUN.3 +140 st11 -- -- +146 Solaris_lun_group Solaris_LUN1_13G -- + solaris_LUN_2_33G +147 wcj_vplex wcj_vplex.0 -- +----------------------------------------------------------- + 32 total 28 +""" +VOLUME_ID_DATAS = """ + Id Name Prov Type CopyOf BsId Rd -Detailed_State- Adm Snp Usr VSize +4836 wcj_2.0 tpvv base --- 4836 RW normal 256 512 512 5120 +4798 zyz_2 tpvv base --- 4836 RW normal 256 512 512 5120 +4797 wcj_3.1 tpvv base --- 4836 RW normal 256 512 512 5120 +666 yytest_vv_001 tpvv base --- 4836 RW normal 256 512 512 5120 +------------------------------------------------------------------------ + 409 total 51072 158720 3279488 18186240 +""" +HOST_DATAS = [ + { + "total": 38, + "members": [ + { + "id": 54, + "name": "Doradov3_lm", + "descriptors": { + "location": "U9-3-B17R_B7", + "IPAddr": "100.157.61.100", + "os": "ESXI6.0", + "model": "RH2288H V3" + }, + "FCPaths": [ + { + "wwn": "2408244427906812", + "hostSpeed": 0 + }, + { + "wwn": "2418244427906812", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 57, + "name": "AIX_wenbin", + "FCPaths": [ + { + "wwn": "10000000C9E74BCC", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 5, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 65, + "name": "SKY-ESXI60", + "descriptors": { + "location": "U9-3-B17R_B7", + "IPAddr": "100.157.61.100", + "os": "ESXI6.0", + "model": "RH2288H V3" + }, + "FCPaths": [ + { + "wwn": "2100001B321BE0FF", + "hostSpeed": 0 + }, + { + "wwn": "2101001B323BE0FF", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 67, + "name": "zouming", + "FCPaths": [ + { + "wwn": "2012E4A8B6B0A1CC", + "hostSpeed": 0 + }, + { + "wwn": "2002E4A8B6B0A1CC", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 68, + "name": "powerpath", + "FCPaths": [ + { + "wwn": "21000024FF36D406", + "hostSpeed": 0 + }, + { + "wwn": "21000024FF36D407", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 69, + "name": "power_v3", + "FCPaths": [ + { + "wwn": "20809CE37435D845", + "hostSpeed": 0 + }, + { + "wwn": "20909CE37435D845", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 89, + "name": "vplex_meta_important", + "FCPaths": [ + { + "wwn": "5000144280292012", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "5000144280292010", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "5000144290292012", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "500014429029E910", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "500014429029E912", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "500014428029E912", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "500014428029E910", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "5000144290292010", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "5000144290292012", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "5000144290292010", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "500014429029E912", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "500014429029E910", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "5000144280292012", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "5000144280292010", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "500014428029E912", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "500014428029E910", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 91, + "name": "Dorado5000_51.45", + "FCPaths": [ + { + "wwn": "200080D4A58EA53A", + "hostSpeed": 0 + }, + { + "wwn": "201080D4A58EA53A", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 98, + "name": "AIX6.1_LN", + "descriptors": { + "os": "AIX" + }, + "FCPaths": [ + { + "wwn": "10000000C9781C57", + "hostSpeed": 0 + }, + { + "wwn": "10000000C9781853", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 5, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 115, + "name": "huhuihost", + "descriptors": { + "os": "SuSE" + }, + "FCPaths": [ + { + "wwn": "2100000E1E1A9B30", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 121, + "name": "Dorado5000V3_F3", + "descriptors": { + "os": "Red Hat Enterprise Linux" + }, + "FCPaths": [ + { + "wwn": "201880D4A58EA53A", + "hostSpeed": 0 + }, + { + "wwn": "200380D4A58EA53A", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 122, + "name": "DYP_RHEL", + "descriptors": { + "IPAddr": "100.157.18.22", + "os": "Red Hat Enterprise Linux" + }, + "FCPaths": [ + { + "wwn": "10000090FA76D446", + "hostSpeed": 0 + }, + { + "wwn": "10000090FA76D447", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 123, + "name": "DYP_Dorado6000", + "FCPaths": [ + { + "wwn": "2618346AC212FB94", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 124, + "name": "tool_rhel6.8", + "FCPaths": [ + { + "wwn": "21000024FF543687", + "hostSpeed": 0 + }, + { + "wwn": "21000024FF543686", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 125, + "name": "OceanStor6800", + "FCPaths": [ + { + "wwn": "2430E0979656725A", + "hostSpeed": 0 + }, + { + "wwn": "2208E0979656725A", + "hostSpeed": 0 + }, + { + "wwn": "2218E0979656725A", + "hostSpeed": 0 + }, + { + "wwn": "2428E0979656725A", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 126, + "name": "fyc_test", + "FCPaths": [ + { + "wwn": "21000024FF41DE7E", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 127, + "name": "huhui", + "descriptors": { + "os": "SuSE" + }, + "FCPaths": [ + { + "wwn": "500601610864241E", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 132, + "name": "ESX8.44.161.152", + "descriptors": { + "os": "ESX 4.x/5.x" + }, + "FCPaths": [ + { + "wwn": "21000024FF2F3266", + "hostSpeed": 0 + }, + { + "wwn": "21000024FF2F3267", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 8, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 133, + "name": "ESX89PT_suse_8.44.190.111", + "descriptors": { + "os": "SuSE" + }, + "FCPaths": [ + { + "wwn": "21000024FF36F1ED", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 134, + "name": "SVC", + "descriptors": { + "os": "Exanet" + }, + "FCPaths": [ + { + "wwn": "500507680110EF7C", + "hostSpeed": 0 + }, + { + "wwn": "500507680120EF7C", + "hostSpeed": 0 + }, + { + "wwn": "500507680120EF3E", + "hostSpeed": 0 + }, + { + "wwn": "500507680110EF3E", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 3, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 135, + "name": "NSS_8.44.162.50", + "descriptors": { + "os": "Red Hat Enterprise Linux" + }, + "FCPaths": [ + { + "wwn": "21000024FF0DC381", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 137, + "name": "D185_8.44.143.201", + "descriptors": { + "os": "Red Hat Enterprise Linux" + }, + "FCPaths": [ + { + "wwn": "29A11603042D0306", + "hostSpeed": 0 + }, + { + "wwn": "28D01603042D0306", + "hostSpeed": 0 + }, + { + "wwn": "2903010203040509", + "hostSpeed": 0 + }, + { + "wwn": "2802010203040509", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 139, + "name": "Dorado3000V6", + "FCPaths": [ + { + "wwn": "2019CC64A68314D3", + "hostSpeed": 0 + }, + { + "wwn": "2009CC64A68314D3", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 141, + "name": "8.44.143.27T2", + "FCPaths": [ + { + "wwn": "10000090FA50C4DF", + "hostSpeed": 0 + }, + { + "wwn": "10000090FA50C4DE", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 142, + "name": "8.44.143.27T1", + "FCPaths": [], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 144, + "name": "C61_51.10.58.190", + "descriptors": { + "os": "Red Hat Enterprise Linux" + }, + "FCPaths": [ + { + "wwn": "2210112224901223", + "hostSpeed": 0 + }, + { + "wwn": "2200112224901223", + "hostSpeed": 0 + }, + { + "wwn": "2230112224901223", + "hostSpeed": 0 + }, + { + "wwn": "2220112224901223", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 145, + "name": "8.44.43.19", + "FCPaths": [ + { + "wwn": "21000024FF754606", + "hostSpeed": 0 + }, + { + "wwn": "21000024FF1A99E1", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 146, + "name": "ZTY_win2012", + "descriptors": { + "os": "Windows 2012" + }, + "FCPaths": [ + { + "wwn": "21000024FF40272B", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "21000024FF40272A", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 2, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 147, + "name": "DoradoV6_183", + "FCPaths": [ + { + "wwn": "240B121314151617", + "hostSpeed": 0 + }, + { + "wwn": "2409121314151617", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 148, + "name": "rhev_125", + "descriptors": { + "os": "Windows 2012" + }, + "FCPaths": [ + { + "wwn": "21000024FF4BC1B7", + "hostSpeed": 0 + }, + { + "wwn": "21000024FF4BC1B6", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 2, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 150, + "name": "windows2012_68", + "descriptors": { + "os": "Windows 2012" + }, + "FCPaths": [ + { + "wwn": "2101001B32B0667A", + "hostSpeed": 0 + }, + { + "wwn": "2100001B3290667A", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 2, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 151, + "name": "Dorado5000V6_80", + "FCPaths": [ + { + "wwn": "2001183D5E0F5131", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "2011183D5E0F5131", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 152, + "name": "windows2012_60", + "descriptors": { + "os": "Windows 2012" + }, + "FCPaths": [ + { + "wwn": "21000024FF53B4BC", + "hostSpeed": 0 + }, + { + "wwn": "21000024FF53B4BD", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 2, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 153, + "name": "aix_8.44.134.204", + "descriptors": { + "os": "AIX" + }, + "FCPaths": [ + { + "wwn": "10000000C975804C", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "10000000C9765E79", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 5, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 154, + "name": "Dorado5500_V6_109", + "descriptors": { + "IPAddr": "8.44.133.82", + "os": "Windows 2012" + }, + "FCPaths": [ + { + "wwn": "221818022D189653", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "220818022D189653", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 2, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 155, + "name": "aix134.205", + "descriptors": { + "IPAddr": "8.44.134.205", + "os": "AIX" + }, + "FCPaths": [ + { + "wwn": "20000000C9781C81", + "hostSpeed": 0 + }, + { + "wwn": "10000000C9781C0C", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 5, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "id": 158, + "name": "hsv6", + "FCPaths": [ + { + "wwn": "28130A2B304438A8", + "hostSpeed": 0 + }, + { + "wwn": "28120A2B304438A8", + "hostSpeed": 0 + }, + { + "wwn": "28F20A2B304438A8", + "hostSpeed": 0 + }, + { + "wwn": "28F30A2B304438A8", + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "persona": 1, + "initiatorChapEnabled": False, + "targetChapEnabled": False + }, + { + "FCPaths": [ + { + "wwn": "21000024FF41DCF7", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "21000024FF41DCF6", + "portPos": { + "node": 1, + "slot": 0, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "21000024FF0CC6CA", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "21000024FF0CC6CA", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "21000024FF0CBF47", + "portPos": { + "node": 0, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + }, + { + "wwn": "21000024FF0CBF47", + "portPos": { + "node": 1, + "slot": 1, + "cardPort": 2 + }, + "hostSpeed": 0 + } + ], + "iSCSIPaths": [], + "initiatorChapEnabled": False, + "targetChapEnabled": False + } + ] + } +] +VIEW_DATAS = """ + Lun VVName HostName -Host_WWN/iSCSI_Name- Port Type + 2 yytest_vv_001 host002 ---------------- 0:2:1 host + 0 set:vvset001 set:hostset111 ---------------- 1:2:1 host set +-------------------------------------------------------------------- + 2 total +""" + CONTROLLER_RESULT = [ { 'name': '1307327-0', @@ -168,7 +1257,7 @@ def __init__(self): 'firmware': 'FW_Rev111', 'speed': 15000, 'capacity': 599684808704, - 'status': 'abnormal', + 'status': 'degraded', 'physical_type': 'fc', 'logical_type': None, 'health_score': None, @@ -195,6 +1284,56 @@ def __init__(self): 'ipv6': None, 'ipv6_mask': None }] +HOST_GROUP_RESULT = [ + { + 'name': 'HostSet_VMware', + 'description': '', + 'storage_id': '12345', + 'native_storage_host_group_id': '194' + }] +VOLUME_GROUP_RESULT = [ + { + 'name': 'wcj_2', + 'description': '', + 'storage_id': '12345', + 'native_volume_group_id': '91' + }] +PORT_GROUP_RESULT = [ + { + 'name': 'port_group_0:2:1', + 'description': 'port_group_0:2:1', + 'storage_id': '12345', + 'native_port_group_id': 'port_group_0:2:1' + }] +HOST_RESULT = [ + { + 'name': 'Doradov3_lm', + 'description': None, + 'storage_id': '12345', + 'native_storage_host_id': 54, + 'os_type': 'VMware ESX', + 'status': 'normal', + 'ip_address': '100.157.61.100' + }] +INITIATOR_RESULT = [ + { + 'name': '2408244427906812', + 'storage_id': '12345', + 'native_storage_host_initiator_id': '2408244427906812', + 'wwn': '2408244427906812', + 'type': 'fc', + 'status': 'online', + 'native_storage_host_id': '175' + }] +VIEW_RESULT = [ + { + 'native_masking_view_id': '2_0:2:1_host002_yytest_vv_001', + 'name': '2', + 'storage_id': '12345', + 'native_port_group_id': 'port_group_0:2:1', + 'native_volume_id': '666', + 'native_storage_host_id': '160' + }] def create_driver(): @@ -605,3 +1744,47 @@ def test_get_ports(self): PORT_RCIP_DATAS, PORT_RCIP_DATAS]) ports = driver.list_ports(context) self.assertDictEqual(ports[0], PORT_RESULT[0]) + + def test_get_storage_host_groups(self): + driver = create_driver() + SSHPool.do_exec = mock.Mock(side_effect=[HOST_GROUP_DATAS, + HOST_ID_DATAS]) + host_groups = driver.list_storage_host_groups(context) + self.assertDictEqual(host_groups.get('storage_host_groups')[0], + HOST_GROUP_RESULT[0]) + + def test_get_volume_groups(self): + driver = create_driver() + SSHPool.do_exec = mock.Mock(side_effect=[VOLUME_GROUP_DATAS, + VOLUME_ID_DATAS]) + volume_groups = driver.list_volume_groups(context) + self.assertDictEqual(volume_groups.get('volume_groups')[0], + VOLUME_GROUP_RESULT[0]) + + def test_storage_hosts(self): + driver = create_driver() + with mock.patch.object(RestHandler, 'get_resinfo_call', + side_effect=HOST_DATAS): + storage_hosts = driver.list_storage_hosts(context) + self.assertDictEqual(storage_hosts[0], HOST_RESULT[0]) + + def test_get_storage_host_initiators(self): + driver = create_driver() + SSHPool.do_exec = mock.Mock(side_effect=[HOST_ID_DATAS]) + initiators = driver.list_storage_host_initiators(context) + self.assertDictEqual(initiators[0], INITIATOR_RESULT[0]) + + def test_get_masking_views(self): + driver = create_driver() + SSHPool.do_exec = mock.Mock( + side_effect=[VIEW_DATAS, HOST_ID_DATAS, HOST_GROUP_DATAS, + VOLUME_ID_DATAS, VOLUME_GROUP_DATAS]) + views = driver.list_masking_views(context) + self.assertDictEqual(views[0], VIEW_RESULT[0]) + + def test_get_port_groups(self): + driver = create_driver() + SSHPool.do_exec = mock.Mock(side_effect=[VIEW_DATAS]) + port_groups = driver.list_port_groups(context) + self.assertDictEqual(port_groups.get('port_groups')[0], + PORT_GROUP_RESULT[0]) From 175fb100473dd72cf925b599a13cc3efba59e697 Mon Sep 17 00:00:00 2001 From: yuanyu-ghca <79956159+yuanyu-ghca@users.noreply.github.com> Date: Tue, 17 May 2022 18:13:23 +0800 Subject: [PATCH 14/17] Hpe 3par add collect performance interface (#789) --- .../drivers/hpe/hpe_3par/component_handler.py | 325 +++++++ delfin/drivers/hpe/hpe_3par/consts.py | 118 +++ delfin/drivers/hpe/hpe_3par/hpe_3parstor.py | 25 +- delfin/drivers/hpe/hpe_3par/rest_handler.py | 20 + delfin/drivers/hpe/hpe_3par/ssh_handler.py | 86 +- delfin/drivers/utils/tools.py | 25 + .../drivers/hpe/hpe_3par/test_hpe_3parstor.py | 835 ++++++++++++++++++ 7 files changed, 1431 insertions(+), 3 deletions(-) diff --git a/delfin/drivers/hpe/hpe_3par/component_handler.py b/delfin/drivers/hpe/hpe_3par/component_handler.py index 5f3dfc4f3..8be778565 100644 --- a/delfin/drivers/hpe/hpe_3par/component_handler.py +++ b/delfin/drivers/hpe/hpe_3par/component_handler.py @@ -11,7 +11,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import copy +import datetime import re +import time import six from oslo_log import log @@ -418,6 +421,328 @@ def parse_speed(self, speed_value): LOG.error(err_msg) return speed + def collect_perf_metrics(self, storage_id, resource_metrics, + start_time, end_time): + metrics = [] + try: + # storage-pool metrics + if resource_metrics.get(constants.ResourceType.STORAGE_POOL): + pool_metrics = self.get_pool_metrics( + storage_id, + resource_metrics.get(constants.ResourceType.STORAGE_POOL), + start_time, end_time) + metrics.extend(pool_metrics) + + # volume metrics + if resource_metrics.get(constants.ResourceType.VOLUME): + volume_metrics = self.get_volume_metrics( + storage_id, + resource_metrics.get(constants.ResourceType.VOLUME), + start_time, end_time) + metrics.extend(volume_metrics) + + # port metrics + if resource_metrics.get(constants.ResourceType.PORT): + port_metrics = self.get_port_metrics( + storage_id, + resource_metrics.get(constants.ResourceType.PORT), + start_time, end_time) + metrics.extend(port_metrics) + + # disk metrics + if resource_metrics.get(constants.ResourceType.DISK): + disk_metrics = self.get_disk_metrics( + storage_id, + resource_metrics.get(constants.ResourceType.DISK), + start_time, end_time) + metrics.extend(disk_metrics) + except exception.DelfinException as err: + err_msg = "Failed to collect metrics from Hpe3parStor: %s" % \ + (six.text_type(err)) + LOG.error(err_msg) + raise err + except Exception as err: + err_msg = "Failed to collect metrics from Hpe3parStor: %s" % \ + (six.text_type(err)) + LOG.error(err_msg) + raise exception.InvalidResults(err_msg) + + return metrics + + def get_pool_metrics(self, storage_id, metric_list, + start_time, end_time): + metrics = [] + obj_metrics = {} + pool_maps = {} + pools = self.rest_handler.get_all_pools() + if pools: + pool_members = pools.get('members') + for pool in pool_members: + pool_maps[pool.get('name')] = str(pool.get('id')) + obj_metrics = self.rest_format_metrics_data( + start_time, end_time, self.rest_handler.get_pool_metrics, + constants.ResourceType.STORAGE_POOL) + + if obj_metrics: + for obj_name in obj_metrics.keys(): + if pool_maps.get(obj_name): + labels = { + 'storage_id': storage_id, + 'resource_type': constants.ResourceType.STORAGE_POOL, + 'resource_id': pool_maps.get(obj_name), + 'type': 'RAW', + 'unit': '' + } + metric_model_list = self._get_metric_model(metric_list, + labels, + obj_metrics.get( + obj_name), + consts.POOL_CAP) + if metric_model_list: + metrics.extend(metric_model_list) + return metrics + + def _get_metric_model(self, metric_list, labels, metric_values, obj_cap): + metric_model_list = [] + for metric_name in (metric_list or []): + values = {} + obj_labels = copy.deepcopy(labels) + obj_labels['unit'] = obj_cap.get(metric_name).get('unit') + for metric_value in metric_values: + if metric_value.get(metric_name) is not None: + collect_timestamp = self.convert_to_system_time( + metric_value.get('collect_timestamp')) + values[collect_timestamp] = metric_value.get( + metric_name) + if values: + metric_model = constants.metric_struct(name=metric_name, + labels=obj_labels, + values=values) + metric_model_list.append(metric_model) + return metric_model_list + + def get_port_metrics(self, storage_id, metric_list, + start_time, end_time): + metrics = [] + obj_metrics = self.ssh_format_metrics_data( + start_time, end_time, self.ssh_handler.get_port_metrics, + constants.ResourceType.PORT) + if obj_metrics: + for obj_id in obj_metrics.keys(): + labels = { + 'storage_id': storage_id, + 'resource_type': constants.ResourceType.PORT, + 'resource_id': obj_id, + 'type': 'RAW', + 'unit': '' + } + metric_model_list = self._get_metric_model(metric_list, + labels, + obj_metrics.get( + obj_id), + consts.PORT_CAP) + if metric_model_list: + metrics.extend(metric_model_list) + return metrics + + def get_disk_metrics(self, storage_id, metric_list, + start_time, end_time): + metrics = [] + obj_metrics = self.ssh_format_metrics_data( + start_time, end_time, self.ssh_handler.get_disk_metrics, + constants.ResourceType.DISK) + if obj_metrics: + for obj_id in obj_metrics.keys(): + labels = { + 'storage_id': storage_id, + 'resource_type': constants.ResourceType.DISK, + 'resource_id': obj_id, + 'type': 'RAW', + 'unit': '' + } + metric_model_list = self._get_metric_model(metric_list, + labels, + obj_metrics.get( + obj_id), + consts.DISK_CAP) + if metric_model_list: + metrics.extend(metric_model_list) + return metrics + + def get_volume_metrics(self, storage_id, metric_list, + start_time, end_time): + metrics = [] + obj_metrics = {} + try: + obj_metrics = self.ssh_format_metrics_data( + start_time, end_time, self.ssh_handler.get_volume_metrics, + constants.ResourceType.VOLUME) + except Exception as err: + err_msg = "Failed to collect volume metrics: %s" \ + % (six.text_type(err)) + LOG.warning(err_msg) + if obj_metrics: + for obj_id in obj_metrics.keys(): + labels = { + 'storage_id': storage_id, + 'resource_type': constants.ResourceType.VOLUME, + 'resource_id': obj_id, + 'type': 'RAW', + 'unit': '' + } + metric_model_list = self._get_metric_model(metric_list, + labels, + obj_metrics.get( + obj_id), + consts.VOLUME_CAP) + if metric_model_list: + metrics.extend(metric_model_list) + return metrics + + def ssh_format_metrics_data(self, start_time, end_time, get_obj_metrics, + obj_type): + collect_resuore_map = {} + obj_metrics = get_obj_metrics(start_time, end_time) + if obj_metrics: + metric_value = obj_metrics[0] + last_time = metric_value.get('collect_time', 0) + first_time = last_time + time_interval = consts.COLLECT_INTERVAL_HIRES + while (last_time - time_interval) > start_time: + next_obj_metrics = get_obj_metrics( + start_time, (last_time - time_interval)) + if next_obj_metrics: + metric_value = next_obj_metrics[0] + last_time = metric_value.get('collect_time', 0) + if last_time > start_time: + time_interval = first_time - last_time + first_time = last_time + obj_metrics.extend(next_obj_metrics) + else: + break + else: + break + + for obj_metric in (obj_metrics or []): + obj_id = '' + if obj_type == constants.ResourceType.DISK: + obj_id = obj_metric.get('pdid') + elif obj_type == constants.ResourceType.PORT: + obj_id = '%s:%s:%s' % ( + obj_metric.get('port_n'), obj_metric.get('port_s'), + obj_metric.get('port_p')) + elif obj_type == constants.ResourceType.VOLUME: + obj_id = obj_metric.get('vvid') + if obj_id: + metric_list = [] + if collect_resuore_map.get(obj_id): + metric_list = collect_resuore_map.get(obj_id) + else: + collect_resuore_map[obj_id] = metric_list + metric_map = {} + metric_map['iops'] = float(obj_metric.get('iotot')) + metric_map['readIops'] = float(obj_metric.get('iord')) + metric_map['writeIops'] = float(obj_metric.get('iowr')) + metric_map['throughput'] = round( + float(obj_metric.get('kbytestot')) / units.k, 5) + metric_map['readThroughput'] = round( + float(obj_metric.get('kbytesrd')) / units.k, 5) + metric_map['writeThroughput'] = round( + float(obj_metric.get('kbyteswr')) / units.k, 5) + metric_map['responseTime'] = float( + obj_metric.get('svcttot')) + metric_map['ioSize'] = float(obj_metric.get('iosztot')) + metric_map['readIoSize'] = float(obj_metric.get('ioszrd')) + metric_map['writeIoSize'] = float(obj_metric.get('ioszwr')) + metric_map['collect_timestamp'] = obj_metric.get( + 'collect_time') + metric_list.append(metric_map) + return collect_resuore_map + + def rest_format_metrics_data(self, start_time, end_time, get_obj_metrics, + obj_type): + collect_resuore_map = {} + obj_metrics_list = [] + obj_metrics = get_obj_metrics(start_time, end_time) + if obj_metrics: + last_time = obj_metrics.get('sampleTimeSec', 0) * units.k + first_time = last_time + time_interval = consts.COLLECT_INTERVAL_HIRES + metric_members = obj_metrics.get('members') + if metric_members: + for member in metric_members: + member['collect_timestamp'] = last_time + obj_metrics_list.extend(metric_members) + while (last_time - time_interval) > start_time: + next_obj_metrics = get_obj_metrics( + start_time, + (last_time - time_interval)) + metric_members = next_obj_metrics.get('members') + if metric_members: + last_time = next_obj_metrics.get( + 'sampleTimeSec', 0) * units.k + if last_time > start_time: + time_interval = first_time - last_time + first_time = last_time + for member in metric_members: + member['collect_timestamp'] = last_time + obj_metrics_list.extend(metric_members) + else: + break + else: + break + for obj_metric in (obj_metrics_list or []): + obj_id = '' + if obj_type == constants.ResourceType.STORAGE_POOL: + obj_id = obj_metric.get('name') + if obj_id: + metric_list = [] + if collect_resuore_map.get(obj_id): + metric_list = collect_resuore_map.get(obj_id) + else: + collect_resuore_map[obj_id] = metric_list + metric_map = {} + metric_map['iops'] = obj_metric.get('IO').get('total') + metric_map['readIops'] = obj_metric.get('IO').get('read') + metric_map['writeIops'] = obj_metric.get('IO').get('write') + metric_map['throughput'] = round( + obj_metric.get('KBytes').get('total') / units.k, 5) + metric_map['readThroughput'] = round( + obj_metric.get('KBytes').get('read') / units.k, 5) + metric_map['writeThroughput'] = round( + obj_metric.get('KBytes').get('write') / units.k, 5) + metric_map['responseTime'] = obj_metric.get( + 'serviceTimeMS').get('total') + metric_map['ioSize'] = obj_metric.get('IOSizeKB').get('total') + metric_map['readIoSize'] = obj_metric.get('IOSizeKB').get( + 'read') + metric_map['writeIoSize'] = obj_metric.get('IOSizeKB').get( + 'write') + metric_map['collect_timestamp'] = obj_metric.get( + 'collect_timestamp') + metric_list.append(metric_map) + return collect_resuore_map + + def get_latest_perf_timestamp(self): + latest_time = 0 + disks_metrics_datas = self.ssh_handler.get_disk_metrics(None, None) + for metrics_data in (disks_metrics_datas or []): + if metrics_data and metrics_data.get('collect_time'): + latest_time = metrics_data.get('collect_time') + break + return latest_time + + def convert_to_system_time(self, occur_time): + dateArray = datetime.datetime.utcfromtimestamp(occur_time / units.k) + otherStyleTime = dateArray.strftime("%Y-%m-%d %H:%M:%SZ") + timeArray = time.strptime(otherStyleTime, "%Y-%m-%d %H:%M:%SZ") + timeStamp = int(time.mktime(timeArray)) + hour_offset = (time.mktime(time.localtime()) - time.mktime( + time.gmtime())) / consts.SECONDS_PER_HOUR + occur_time = timeStamp * units.k + (int(hour_offset) * + consts.SECONDS_PER_HOUR) * units.k + return occur_time + def list_storage_host_initiators(self, storage_id): initiators = self.ssh_handler.list_storage_host_initiators() initiators_list = [] diff --git a/delfin/drivers/hpe/hpe_3par/consts.py b/delfin/drivers/hpe/hpe_3par/consts.py index 895573929..8cabc8957 100644 --- a/delfin/drivers/hpe/hpe_3par/consts.py +++ b/delfin/drivers/hpe/hpe_3par/consts.py @@ -47,6 +47,10 @@ # session SUCCESS's status LOGIN_SUCCESS_STATUS_CODES = 201 +SERVICE_UNAVAILABLE_CODES = 503 +BAD_REQUEST_CODES = 400 +NOT_IMPLEMENTED_CODES = 501 + # alert state enumeration ALERT_STATE_NEW = 1 # New. ALERT_STATE_ACKED = 2 # Acknowledged state. @@ -654,6 +658,14 @@ FSHARE_PATTERN = "^\\s*ShareName\\s+Protocol\\s+VFS\\s+FileStore\\s+" \ "ShareDir\\s+State" VFS_PATTERN = "^\\s*VFS\\s+FPG\\s+IPAddr\\s+State" + +SRSTATPORT_PATTERN = "^\\s*PORT_N\\s+PORT_S\\s+PORT_P\\s+Rd\\s+Wr\\s+" \ + "Tot\\s+Rd\\s+Wr\\s+Tot\\s+Rd\\s+Wr\\s+Tot" +SRSTATPD_PATTERN = "^\\s*PDID\\s+Rd\\s+Wr\\s+" \ + "Tot\\s+Rd\\s+Wr\\s+Tot\\s+Rd\\s+Wr\\s+Tot" +SRSTATVV_PATTERN = "^\\s*VVID\\s+VV_NAME\\s+Rd\\s+Wr\\s+" \ + "Tot\\s+Rd\\s+Wr\\s+Tot\\s+Rd\\s+Wr\\s+Tot" + IPV4_PATTERN = "^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$" HOST_OR_VV_SET_PATTERN = "^\\s*Id\\s+Name\\s+Members\\s+Comment" @@ -710,6 +722,112 @@ 1: "control", 2: "data" } +SSH_METRIC_TYPE = { + 1: "io", + 2: "kbytes", + 3: "svct", + 4: "iosz" +} +SSH_COLLECT_TIME_PATTERN = "\\(\\d+\\)" +COLLECT_INTERVAL_HIRES = 60000 +SIXTY_SECONDS = 60 +REST_COLLEC_TTIME_PATTERN = '%Y-%m-%dT%H:%M:%SZ' +IOPS_DESCRIPTION = { + "unit": "IOPS", + "description": "Input/output operations per second" +} +READ_IOPS_DESCRIPTION = { + "unit": "IOPS", + "description": "Read input/output operations per second" +} +WRITE_IOPS_DESCRIPTION = { + "unit": "IOPS", + "description": "Write input/output operations per second" +} +THROUGHPUT_DESCRIPTION = { + "unit": "MB/s", + "description": "Represents how much data is " + "successfully transferred in MB/s" +} +READ_THROUGHPUT_DESCRIPTION = { + "unit": "MB/s", + "description": "Represents how much data read is " + "successfully transferred in MB/s" +} +WRITE_THROUGHPUT_DESCRIPTION = { + "unit": "MB/s", + "description": "Represents how much data write is " + "successfully transferred in MB/s" +} +RESPONSE_TIME_DESCRIPTION = { + "unit": "ms", + "description": "Average time taken for an IO " + "operation in ms" +} +CACHE_HIT_RATIO_DESCRIPTION = { + "unit": "%", + "description": "Percentage of io that are cache hits" +} +READ_CACHE_HIT_RATIO_DESCRIPTION = { + "unit": "%", + "description": "Percentage of read ops that are cache hits" +} +WRITE_CACHE_HIT_RATIO_DESCRIPTION = { + "unit": "%", + "description": "Percentage of write ops that are cache hits" +} +IO_SIZE_DESCRIPTION = { + "unit": "KB", + "description": "The average size of IO requests in KB" +} +READ_IO_SIZE_DESCRIPTION = { + "unit": "KB", + "description": "The average size of read IO requests in KB" +} +WRITE_IO_SIZE_DESCRIPTION = { + "unit": "KB", + "description": "The average size of write IO requests in KB" +} +POOL_CAP = { + "iops": IOPS_DESCRIPTION, + "readIops": READ_IOPS_DESCRIPTION, + "writeIops": WRITE_IOPS_DESCRIPTION, + "throughput": THROUGHPUT_DESCRIPTION, + "readThroughput": READ_THROUGHPUT_DESCRIPTION, + "writeThroughput": WRITE_THROUGHPUT_DESCRIPTION, + "responseTime": RESPONSE_TIME_DESCRIPTION +} +VOLUME_CAP = { + "iops": IOPS_DESCRIPTION, + "readIops": READ_IOPS_DESCRIPTION, + "writeIops": WRITE_IOPS_DESCRIPTION, + "throughput": THROUGHPUT_DESCRIPTION, + "readThroughput": READ_THROUGHPUT_DESCRIPTION, + "writeThroughput": WRITE_THROUGHPUT_DESCRIPTION, + "responseTime": RESPONSE_TIME_DESCRIPTION, + "ioSize": IO_SIZE_DESCRIPTION, + "readIoSize": READ_IO_SIZE_DESCRIPTION, + "writeIoSize": WRITE_IO_SIZE_DESCRIPTION +} +PORT_CAP = { + "iops": IOPS_DESCRIPTION, + "readIops": READ_IOPS_DESCRIPTION, + "writeIops": WRITE_IOPS_DESCRIPTION, + "throughput": THROUGHPUT_DESCRIPTION, + "readThroughput": READ_THROUGHPUT_DESCRIPTION, + "writeThroughput": WRITE_THROUGHPUT_DESCRIPTION, + "responseTime": RESPONSE_TIME_DESCRIPTION +} +DISK_CAP = { + "iops": IOPS_DESCRIPTION, + "readIops": READ_IOPS_DESCRIPTION, + "writeIops": WRITE_IOPS_DESCRIPTION, + "throughput": THROUGHPUT_DESCRIPTION, + "readThroughput": READ_THROUGHPUT_DESCRIPTION, + "writeThroughput": WRITE_THROUGHPUT_DESCRIPTION, + "responseTime": RESPONSE_TIME_DESCRIPTION +} +SECONDS_PER_HOUR = 3600 HOST_OS_MAP = { 'AIX': constants.HostOSTypes.AIX, 'Citrix Xen Server 5.x/6.x': constants.HostOSTypes.XEN_SERVER, diff --git a/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py b/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py index e425046ed..91a303e46 100644 --- a/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py +++ b/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py @@ -16,8 +16,9 @@ from oslo_log import log from delfin import context +from delfin.common import constants from delfin.drivers import driver -from delfin.drivers.hpe.hpe_3par import alert_handler +from delfin.drivers.hpe.hpe_3par import alert_handler, consts from delfin.drivers.hpe.hpe_3par import component_handler from delfin.drivers.hpe.hpe_3par import rest_handler from delfin.drivers.hpe.hpe_3par import ssh_handler @@ -102,6 +103,28 @@ def list_storage_host_initiators(self, context): def list_storage_hosts(self, context): return self.comhandler.list_storage_hosts(self.storage_id) + def collect_perf_metrics(self, context, storage_id, resource_metrics, + start_time, end_time): + return self.comhandler.collect_perf_metrics(storage_id, + resource_metrics, + start_time, end_time) + + @staticmethod + def get_capabilities(context, filters=None): + """Get capability of supported driver""" + return { + 'is_historic': True, + 'resource_metrics': { + constants.ResourceType.STORAGE_POOL: consts.POOL_CAP, + constants.ResourceType.VOLUME: consts.VOLUME_CAP, + constants.ResourceType.PORT: consts.PORT_CAP, + constants.ResourceType.DISK: consts.DISK_CAP + } + } + + def get_latest_perf_timestamp(self, context): + return self.comhandler.get_latest_perf_timestamp() + def list_storage_host_groups(self, context): return self.comhandler.list_storage_host_groups(self.storage_id) diff --git a/delfin/drivers/hpe/hpe_3par/rest_handler.py b/delfin/drivers/hpe/hpe_3par/rest_handler.py index 0fde1bf84..f73ba15ba 100644 --- a/delfin/drivers/hpe/hpe_3par/rest_handler.py +++ b/delfin/drivers/hpe/hpe_3par/rest_handler.py @@ -21,6 +21,7 @@ from delfin import cryptor from delfin import exception from delfin.drivers.hpe.hpe_3par import consts +from delfin.drivers.utils.tools import Tools LOG = logging.getLogger(__name__) @@ -42,6 +43,10 @@ class RestHandler(object): REST_AUTH_KEY = 'X-HP3PAR-WSAPI-SessionKey' + REST_CPGSTATISTICS_URL = '/api/v1/systemreporter' \ + '/attime/cpgstatistics/hires?' \ + 'query="sampleTime GE %s AND sampleTime LE %s"' + session_lock = None def __init__(self, rest_client): @@ -105,6 +110,11 @@ def get_resinfo_call(self, url, data=None, method=None): if res is not None: if res.status_code == consts.SUCCESS_STATUS_CODES: rejson = res.json() + else: + if res.text and 'unsupported' in res.text: + LOG.warning('rest api error: {}'.format(res.text)) + else: + raise exception.StorageBackendException(res.text) return rejson def login(self): @@ -199,6 +209,16 @@ def get_all_volumes(self): method='GET') return rejson + def get_pool_metrics(self, start_time, end_time): + start_time_str = Tools.timestamp_to_utc_time_str( + start_time, consts.REST_COLLEC_TTIME_PATTERN) + end_time_str = Tools.timestamp_to_utc_time_str( + end_time, consts.REST_COLLEC_TTIME_PATTERN) + url = RestHandler.REST_CPGSTATISTICS_URL % ( + start_time_str, end_time_str) + rejson = self.get_resinfo_call(url, method='GET') + return rejson + def list_storage_host(self): rejson = self.get_resinfo_call(RestHandler.REST_HOSTS_URL, method='GET') diff --git a/delfin/drivers/hpe/hpe_3par/ssh_handler.py b/delfin/drivers/hpe/hpe_3par/ssh_handler.py index 095cffee2..47003de22 100644 --- a/delfin/drivers/hpe/hpe_3par/ssh_handler.py +++ b/delfin/drivers/hpe/hpe_3par/ssh_handler.py @@ -14,14 +14,17 @@ # License for the specific language governing permissions and limitations # under the License. import re +import time import six from oslo_log import log as logging +from oslo_utils import units from delfin import exception from delfin import utils from delfin.drivers.hpe.hpe_3par import consts from delfin.drivers.utils.ssh_client import SSHPool +from delfin.drivers.utils.tools import Tools LOG = logging.getLogger(__name__) @@ -54,6 +57,14 @@ class SSHHandler(object): HPE3PAR_COMMAND_SHOWVV = 'showvv' HPE3PAR_COMMAND_SHOWVLUN_T = 'showvlun -t' + HPE3PAR_COMMAND_SHOWVV = 'showvv' + HPE3PAR_COMMAND_SRSTATPORT = 'srstatport -attime -groupby ' \ + 'PORT_N,PORT_S,PORT_P -btsecs %d -etsecs %d' + HPE3PAR_COMMAND_SRSTATPD = 'srstatpd -attime -btsecs %d -etsecs %d' + HPE3PAR_COMMAND_SRSTATVV = 'srstatvv -attime -groupby VVID,VV_NAME' \ + ' -btsecs %d -etsecs %d' + HPE3PAR_COMMAND_SRSTATPD_ATTIME = 'srstatpd -attime' + def __init__(self, **kwargs): self.kwargs = kwargs self.ssh_pool = SSHPool(**kwargs) @@ -293,6 +304,24 @@ def parse_datas_to_list(self, resource_info, pattern_str, para_map=None): str_info, obj_list, titles) + elif para_map and para_map.get('command', '') \ + == 'parse_metric_table': + if '---------------------------------' in str_line: + break + if 'Time:' in str_line: + collect_time = Tools.get_numbers_in_brackets( + str_line, consts.SSH_COLLECT_TIME_PATTERN) + if collect_time: + collect_time = int(collect_time) * units.k + else: + collect_time = int(time.time() * units.k) + para_map['collect_time'] = collect_time + obj_list = self.parse_metric_table(cols_size, + titles_size, + str_info, + obj_list, + titles, + para_map) elif para_map and para_map.get('command', '') \ == 'parse_set_groups_table': if '---------------------------------' in str_line: @@ -405,6 +434,25 @@ def parse_node_cpu(self, cols_size, titles_size, str_info, obj_map): obj_map[node_id] = cpu_info_map return obj_map + def parse_metric_table(self, cols_size, titles_size, str_info, + obj_list, titles, para_map): + if cols_size == titles_size: + obj_model = {} + metric_type_num = 1 + key_prefix = '' + for i in range(0, cols_size): + key = titles[i].lower().replace('-', '') + if key == 'rd': + key_prefix = consts.SSH_METRIC_TYPE.get(metric_type_num) + metric_type_num += 1 + key = '%s%s' % (key_prefix, key) + obj_model[key] = str_info[i] + if obj_model: + if para_map and para_map.get('collect_time'): + obj_model['collect_time'] = para_map.get('collect_time') + obj_list.append(obj_model) + return obj_list + def get_index_of_key(self, titles_list, key): if titles_list: for title in titles_list: @@ -429,14 +477,48 @@ def get_resources_info(self, command, parse_type, pattern_str=None, def exec_command(self, command): re = self.ssh_pool.do_exec(command) if re: - if 'invalid command name' in re: - LOG.error(re) + if 'invalid command name' in re or 'Invalid option' in re: + LOG.warning(re) raise NotImplementedError(re) elif 'Too many local CLI connections' in re: LOG.error("command %s failed: %s" % (command, re)) raise exception.StorageBackendException(re) return re + def get_volumes(self): + return self.get_resources_info(SSHHandler.HPE3PAR_COMMAND_SHOWVV, + self.parse_datas_to_list, + pattern_str=consts.VOLUME_PATTERN) + + def get_port_metrics(self, start_time, end_time): + command = SSHHandler.HPE3PAR_COMMAND_SRSTATPORT % ( + int(start_time / units.k), int(end_time / units.k)) + return self.get_resources_info(command, + self.parse_datas_to_list, + pattern_str=consts.SRSTATPORT_PATTERN, + para_map={ + 'command': 'parse_metric_table'}) + + def get_disk_metrics(self, start_time, end_time): + command = SSHHandler.HPE3PAR_COMMAND_SRSTATPD_ATTIME + if start_time and end_time: + command = SSHHandler.HPE3PAR_COMMAND_SRSTATPD % ( + int(start_time / units.k), int(end_time / units.k)) + return self.get_resources_info(command, + self.parse_datas_to_list, + pattern_str=consts.SRSTATPD_PATTERN, + para_map={ + 'command': 'parse_metric_table'}) + + def get_volume_metrics(self, start_time, end_time): + command = SSHHandler.HPE3PAR_COMMAND_SRSTATVV % ( + int(start_time / units.k), int(end_time / units.k)) + return self.get_resources_info(command, + self.parse_datas_to_list, + pattern_str=consts.SRSTATVV_PATTERN, + para_map={ + 'command': 'parse_metric_table'}) + def list_storage_host_groups(self): para_map = { 'command': 'parse_set_groups_table' diff --git a/delfin/drivers/utils/tools.py b/delfin/drivers/utils/tools.py index 93a172995..969d285cf 100644 --- a/delfin/drivers/utils/tools.py +++ b/delfin/drivers/utils/tools.py @@ -11,7 +11,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import datetime import os +import re import time import six @@ -50,6 +52,17 @@ def timestamp_to_time_str(self, time_stamp, time_pattern): time_str = time.strftime(time_pattern, time_array) return time_str + @staticmethod + def timestamp_to_utc_time_str(time_stamp, time_pattern): + """ Time stamp to time str conversion + """ + time_str = '' + if time_stamp: + time_stamp = time_stamp / units.k + dateArray = datetime.datetime.utcfromtimestamp(time_stamp) + time_str = dateArray.strftime(time_pattern) + return time_str + @staticmethod def change_capacity_to_bytes(unit): unit = unit.upper() @@ -107,6 +120,18 @@ def split_value_map_list(value_info, map_list, is_alert=False, split=":"): map_list.append(value_map) return map_list + @staticmethod + def get_numbers_in_brackets(source_info, pattern_str): + """Get the contents in brackets through regular expressions. + source_info:Source data, example: "collect time (1583012100)" + pattern_str: regular expression. example:"\\(\\d+\\)" + """ + object_info = '' + object_infos = re.findall(pattern_str, source_info) + if object_infos: + object_info = object_infos[0].replace('(', '').replace(')', '') + return object_info + @staticmethod def remove_file_with_same_type(file_name, file_path): file_type = '%s_%s_%s' % (file_name.split('_')[0], diff --git a/delfin/tests/unit/drivers/hpe/hpe_3par/test_hpe_3parstor.py b/delfin/tests/unit/drivers/hpe/hpe_3par/test_hpe_3parstor.py index 700fc1462..a5686aa04 100644 --- a/delfin/tests/unit/drivers/hpe/hpe_3par/test_hpe_3parstor.py +++ b/delfin/tests/unit/drivers/hpe/hpe_3par/test_hpe_3parstor.py @@ -16,6 +16,8 @@ import paramiko +from delfin.common import constants + sys.modules['delfin.cryptor'] = mock.Mock() from delfin import exception from delfin import context @@ -146,6 +148,743 @@ def __init__(self): 108 """ +POOL_DATAS = ret = { + "total": 12, + "members": [ + { + "id": 0, + "uuid": "aa43f218-d3dd-4626-948f-8a160b0eac1d", + "name": "Lcltest333", + "numFPVVs": 21, + "numTPVVs": 25, + "UsrUsage": { + "totalMiB": 1381504, + "rawTotalMiB": 1842004, + "usedMiB": 1376128, + "rawUsedMiB": 712703 + }, + "SAUsage": { + "totalMiB": 140800, + "rawTotalMiB": 422400, + "usedMiB": 5120, + "rawUsedMiB": 15360 + }, + "SDUsage": { + "totalMiB": 388736, + "rawTotalMiB": 518315, + "usedMiB": 0, + "rawUsedMiB": 0 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 4, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 1, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "cpg_Migration1", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 2, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "cpg_Oracle", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 3, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "cpg_filesystem", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 4, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "cpg_test", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 5, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "fs_cpg", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 6, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "ljn2", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 7, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "ljn4_xiuGai", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 8, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "ljn_330", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 9, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "xulin_cpg1", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 10, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "zyz", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + }, + { + "id": 11, + "uuid": "c392910e-7648-4972-b594-47dd3d28f3ec", + "name": "22", + "numFPVVs": 14, + "numTPVVs": 319, + "UsrUsage": { + "totalMiB": 1418752, + "rawTotalMiB": 1702500, + "usedMiB": 1417984, + "rawUsedMiB": 568934 + }, + "SAUsage": { + "totalMiB": 56832, + "rawTotalMiB": 170496, + "usedMiB": 42752, + "rawUsedMiB": 128256 + }, + "SDUsage": { + "totalMiB": 187648, + "rawTotalMiB": 225179, + "usedMiB": 157184, + "rawUsedMiB": 188620 + }, + "SAGrowth": { + "incrementMiB": 8192, + "LDLayout": { + "HA": 3, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "SDGrowth": { + "incrementMiB": 32768, + "LDLayout": { + "RAIDType": 3, + "HA": 3, + "setSize": 6, + "chunkletPosPref": 1, + "diskPatterns": [ + { + "diskType": 1 + } + ] + } + }, + "state": 1, + "failedStates": [], + "degradedStates": [], + "additionalStates": [] + } + ] +} +POOL_METRICS_DATAS = { + "sampleTime": "2020-03-01T03:50:00+08:00", + "sampleTimeSec": 1583005800, + "total": 2, + "members": [ + { + "name": "22", + "IO": { + "read": 0, + "write": 0, + "total": 10 + }, + "KBytes": { + "read": 0, + "write": 0, + "total": 0 + }, + "serviceTimeMS": { + "read": 0, + "write": 0, + "total": 0 + }, + "IOSizeKB": { + "read": 0, + "write": 0, + "total": 0 + }, + "queueLength": 0, + "busyPct": 0 + }, + { + "name": "Lcltest333", + "IO": { + "read": 0, + "write": 0, + "total": 20 + }, + "KBytes": { + "read": 0, + "write": 0, + "total": 0 + }, + "serviceTimeMS": { + "read": 0, + "write": 0, + "total": 0 + }, + "IOSizeKB": { + "read": 0, + "write": 0, + "total": 0 + }, + "queueLength": 0, + "busyPct": 0 + } + ] +} +PORT_METRICS_DATAS = """ +Time: 2021-07-14 14:10:00 CST (1626243000) + ----IO/s----- ---KBytes/s---- ----Svct ms----- -IOSz KBytes- +PORT_N PORT_S PORT_P Rd Wr Tot Rd Wr Tot Rd Wr Tot Rd Wr Tot QLen AvgBusy% +0 0 1 0.0 0.0 0.0 0.0 0.0 0.0 0.00 0.00 0.00 0.0 0.0 0.0 0 0.0 +0 1 1 0.0 14.3 14.3 0.0 86.4 86.4 0.00 11.52 11.52 0.0 6.1 6.1 1 11.9 +---------------------------------------------------------------------------- + 2 7.6 31.4 39.0 0.6 192.0 192.6 0.00 12.34 9.93 0.1 6.2 5.0 1 3.0 +""" +DISK_METRICS_DATAS = """ +Time: 2021-07-14 15:35:00 CST (1626248100) + ----IO/s----- ---KBytes/s---- ----Svct ms----- -IOSz KBytes- +PDID Rd Wr Tot Rd Wr Tot Rd Wr Tot Rd Wr Tot QLen AvgBusy% + 0 0.0 0.5 0.5 0.0 4.9 4.9 0.00 3.04 3.04 0.0 10.0 10.0 0 0.1 + 1 0.0 1.6 1.6 0.0 10.2 10.2 0.00 0.89 0.89 0.0 6.3 6.3 0 0.1 +------------------------------------------------------------------------------- + 2 0.0 31.4 31.4 0.0 191.4 191.4 0.00 11.98 11.98 0.0 6.2 6.2 0 1.5 +""" +VOLUME_METRICS_DATAS = """ +Time: 2021-07-14 14:10:00 CST (1626243000) + ----IO/s----- ---KBytes/s---- ----Svct ms----- -IOSz KBytes- +VVID VV_NAME Rd Wr Tot Rd Wr Tot Rd Wr Tot Rd Wr Tot QLen AvgBusy% +0 srdata 0.0 1.0 2.0 3.0 11.0 22.0 33.00 111.00 222.00 333.0 0.0 0.0 0 0.0 +1 admin 0.0 14.3 14.3 0.0 86.4 86.4 0.00 11.52 11.52 0.0 6.1 6.1 1 11.9 +---------------------------------------------------------------------------- + 2 7.6 31.4 39.0 0.6 192.0 192.6 0.00 12.34 9.93 0.1 6.2 5.0 1 3.0 +""" HOST_GROUP_DATAS = """ Id Name Members Comment 194 HostSet_VMware Host_ESXi6.5_125 -- @@ -1284,6 +2023,46 @@ def __init__(self): 'ipv6': None, 'ipv6_mask': None }] +METRICS_RESULT = [ + constants.metric_struct(name='iops', + labels={ + 'storage_id': '12345', + 'resource_type': 'storagePool', + 'resource_id': '11', + 'type': 'RAW', + 'unit': 'IOPS'}, + values={1583005800000: 10} + ), + constants.metric_struct(name='iops', + labels={ + 'storage_id': '12345', + 'resource_type': 'volume', + 'resource_id': '0', + 'type': 'RAW', + 'unit': 'IOPS'}, + values={1626243000000: 2.0} + ), + constants.metric_struct(name='iops', + labels={ + 'storage_id': '12345', + 'resource_type': 'port', + 'resource_id': '0:0:1', + 'type': 'RAW', + 'unit': 'IOPS' + }, + values={1626243000000: 0.0} + ), + constants.metric_struct(name='iops', + labels={ + 'storage_id': '12345', + 'resource_type': 'disk', + 'resource_id': '0', + 'type': 'RAW', + 'unit': 'IOPS' + }, + values={1626248100000: 0.5} + ), +] HOST_GROUP_RESULT = [ { 'name': 'HostSet_VMware', @@ -1745,6 +2524,62 @@ def test_get_ports(self): ports = driver.list_ports(context) self.assertDictEqual(ports[0], PORT_RESULT[0]) + @mock.patch.object(RestHandler, 'get_pool_metrics') + @mock.patch.object(SSHPool, 'do_exec') + def test_get_perf_metrics(self, mock_exec, mock_pool): + driver = create_driver() + resource_metrics = { + 'storagePool': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput', + 'responseTime' + ], + 'volume': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput', + 'responseTime', + 'ioSize', 'readIoSize', 'writeIoSize', + ], + 'port': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput', + 'responseTime' + ], + 'disk': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput', + 'responseTime' + ], + 'filesystem': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput', + 'readResponseTime', 'writeResponseTime', + 'readIoSize', 'writeIoSize' + ] + } + start_time = 1628472280000 + end_time = 1628472900000 + RestHandler.get_all_pools = mock.Mock(return_value=POOL_DATAS) + mock_pool.return_value = POOL_METRICS_DATAS + mock_exec.side_effect = [VOLUME_METRICS_DATAS, PORT_METRICS_DATAS, + DISK_METRICS_DATAS] + metrics = driver.collect_perf_metrics(context, '12345', + resource_metrics, start_time, + end_time) + self.assertEqual(metrics[0], METRICS_RESULT[0]) + self.assertEqual(metrics[14], METRICS_RESULT[1]) + self.assertEqual(metrics[34], METRICS_RESULT[2]) + self.assertEqual(metrics[48], METRICS_RESULT[3]) + + def test_get_capabilities(self): + driver = create_driver() + cap = driver.get_capabilities(context) + self.assertIsNotNone(cap.get('resource_metrics')) + self.assertIsNotNone(cap.get('resource_metrics').get('storagePool')) + self.assertIsNotNone(cap.get('resource_metrics').get('volume')) + self.assertIsNotNone(cap.get('resource_metrics').get('port')) + self.assertIsNotNone(cap.get('resource_metrics').get('disk')) + def test_get_storage_host_groups(self): driver = create_driver() SSHPool.do_exec = mock.Mock(side_effect=[HOST_GROUP_DATAS, From b92804f27fa769066b8387c2b27a939afab7091d Mon Sep 17 00:00:00 2001 From: tanjiangyu-ghca <79631193+tanjiangyu-ghca@users.noreply.github.com> Date: Wed, 18 May 2022 16:05:55 +0800 Subject: [PATCH 15/17] emc unity support performance (#792) --- delfin/drivers/dell_emc/unity/consts.py | 109 ++ delfin/drivers/dell_emc/unity/rest_handler.py | 32 +- delfin/drivers/dell_emc/unity/unity.py | 448 +++++++- .../drivers/dell_emc/unity/test_emc_unity.py | 993 ++++++++++++++++++ 4 files changed, 1533 insertions(+), 49 deletions(-) diff --git a/delfin/drivers/dell_emc/unity/consts.py b/delfin/drivers/dell_emc/unity/consts.py index c4d848246..cde044ebc 100644 --- a/delfin/drivers/dell_emc/unity/consts.py +++ b/delfin/drivers/dell_emc/unity/consts.py @@ -15,6 +15,7 @@ DEFAULT_TIMEOUT = 10 ALERT_TIMEOUT = 20 +REST_RETRY_TIMES = 1 TRAP_DESC = { "1:127486a": ["WARNING", "ALRT_LCC_FW_UPGRADE_FAILED", "The link control card (LCC) will continue to function with " @@ -3454,3 +3455,111 @@ "Performance metrics are unavailable due to a system error." " Contact your service provider."] } +IOPS_DESCRIPTION = { + "unit": "IOPS", + "description": "Input/output operations per second" +} +READ_IOPS_DESCRIPTION = { + "unit": "IOPS", + "description": "Read input/output operations per second" +} +WRITE_IOPS_DESCRIPTION = { + "unit": "IOPS", + "description": "Write input/output operations per second" +} +THROUGHPUT_DESCRIPTION = { + "unit": "MB/s", + "description": "Represents how much data is " + "successfully transferred in MB/s" +} +READ_THROUGHPUT_DESCRIPTION = { + "unit": "MB/s", + "description": "Represents how much data read is " + "successfully transferred in MB/s" +} +WRITE_THROUGHPUT_DESCRIPTION = { + "unit": "MB/s", + "description": "Represents how much data write is " + "successfully transferred in MB/s" +} +RESPONSE_TIME_DESCRIPTION = { + "unit": "ms", + "description": "Average time taken for an IO " + "operation in ms" +} +CACHE_HIT_RATIO_DESCRIPTION = { + "unit": "%", + "description": "Percentage of io that are cache hits" +} +READ_CACHE_HIT_RATIO_DESCRIPTION = { + "unit": "%", + "description": "Percentage of read ops that are cache hits" +} +WRITE_CACHE_HIT_RATIO_DESCRIPTION = { + "unit": "%", + "description": "Percentage of write ops that are cache hits" +} +IO_SIZE_DESCRIPTION = { + "unit": "KB", + "description": "The average size of IO requests in KB" +} +READ_IO_SIZE_DESCRIPTION = { + "unit": "KB", + "description": "The average size of read IO requests in KB" +} +WRITE_IO_SIZE_DESCRIPTION = { + "unit": "KB", + "description": "The average size of write IO requests in KB" +} +CPU_USAGE_DESCRIPTION = { + "unit": "%", + "description": "Percentage of CPU usage" +} +MEMORY_USAGE_DESCRIPTION = { + "unit": "%", + "description": "Percentage of DISK memory usage in percentage" +} +SERVICE_TIME = { + "unit": 'ms', + "description": "Service time of the resource in ms" +} +VOLUME_CAP = { + "iops": IOPS_DESCRIPTION, + "readIops": READ_IOPS_DESCRIPTION, + "writeIops": WRITE_IOPS_DESCRIPTION, + "throughput": THROUGHPUT_DESCRIPTION, + "readThroughput": READ_THROUGHPUT_DESCRIPTION, + "writeThroughput": WRITE_THROUGHPUT_DESCRIPTION, + "responseTime": RESPONSE_TIME_DESCRIPTION, + "ioSize": IO_SIZE_DESCRIPTION, + "readIoSize": READ_IO_SIZE_DESCRIPTION, + "writeIoSize": WRITE_IO_SIZE_DESCRIPTION +} +PORT_CAP = { + "iops": IOPS_DESCRIPTION, + "readIops": READ_IOPS_DESCRIPTION, + "writeIops": WRITE_IOPS_DESCRIPTION, + "throughput": THROUGHPUT_DESCRIPTION, + "readThroughput": READ_THROUGHPUT_DESCRIPTION, + "writeThroughput": WRITE_THROUGHPUT_DESCRIPTION +} +DISK_CAP = { + "iops": IOPS_DESCRIPTION, + "readIops": READ_IOPS_DESCRIPTION, + "writeIops": WRITE_IOPS_DESCRIPTION, + "throughput": THROUGHPUT_DESCRIPTION, + "readThroughput": READ_THROUGHPUT_DESCRIPTION, + "writeThroughput": WRITE_THROUGHPUT_DESCRIPTION, + "responseTime": RESPONSE_TIME_DESCRIPTION +} +FILESYSTEM_CAP = { + "iops": IOPS_DESCRIPTION, + "readIops": READ_IOPS_DESCRIPTION, + "writeIops": WRITE_IOPS_DESCRIPTION, + "throughput": THROUGHPUT_DESCRIPTION, + "readThroughput": READ_THROUGHPUT_DESCRIPTION, + "writeThroughput": WRITE_THROUGHPUT_DESCRIPTION, + "ioSize": IO_SIZE_DESCRIPTION, + "readIoSize": READ_IO_SIZE_DESCRIPTION, + "writeIoSize": WRITE_IO_SIZE_DESCRIPTION +} diff --git a/delfin/drivers/dell_emc/unity/rest_handler.py b/delfin/drivers/dell_emc/unity/rest_handler.py index 6533d4583..c9e28239a 100644 --- a/delfin/drivers/dell_emc/unity/rest_handler.py +++ b/delfin/drivers/dell_emc/unity/rest_handler.py @@ -47,6 +47,7 @@ class RestHandler(RestClient): REST_QTREE_URL = '/api/types/treeQuota/instances' REST_USERQUOTA_URL = '/api/types/userQuota/instances' REST_QUOTACONFIG_URL = '/api/types/quotaConfig/instances' + REST_VIRTUAL_DISK_URL = '/api/types/virtualDisk/instances' STATE_SOLVED = 2 def __init__(self, **kwargs): @@ -106,11 +107,19 @@ def logout(self): def get_rest_info(self, url, data=None, method='GET', calltimeout=consts.DEFAULT_TIMEOUT): - result_json = None - res = self.call(url, data, method, calltimeout) - if res.status_code == 200: - result_json = res.json() - return result_json + retry_times = consts.REST_RETRY_TIMES + while retry_times >= 0: + try: + res = self.call(url, data, method, calltimeout) + if res.status_code == 200: + return res.json() + err_msg = "rest response abnormal,status_code:%s,res.json:%s" \ + % (res.status_code, res.json()) + LOG.error(err_msg) + except Exception as e: + LOG.error(e) + retry_times -= 1 + return None def call(self, url, data=None, method='GET', calltimeout=consts.DEFAULT_TIMEOUT): @@ -271,3 +280,16 @@ def get_quota_configs(self): 'fields=id,filesystem,treeQuota,quotaPolicy') 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) + result_json = self.get_rest_info(url) + return result_json + + def get_virtual_disks(self): + url = '%s?%s' % (RestHandler.REST_VIRTUAL_DISK_URL, + 'fields=health,name,spaScsiId,tierType,sizeTotal,' + 'id,model,manufacturer,wwn') + result_json = self.get_rest_info(url) + return result_json diff --git a/delfin/drivers/dell_emc/unity/unity.py b/delfin/drivers/dell_emc/unity/unity.py index b6659d851..1edb159d6 100644 --- a/delfin/drivers/dell_emc/unity/unity.py +++ b/delfin/drivers/dell_emc/unity/unity.py @@ -11,6 +11,8 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +import time + import six from oslo_log import log from oslo_utils import units @@ -18,7 +20,7 @@ from delfin import exception from delfin.common import constants from delfin.drivers import driver -from delfin.drivers.dell_emc.unity import rest_handler, alert_handler +from delfin.drivers.dell_emc.unity import rest_handler, alert_handler, consts from delfin.drivers.dell_emc.unity.alert_handler import AlertHandler LOG = log.getLogger(__name__) @@ -53,6 +55,57 @@ class UnityStorDriver(driver.StorageDriver): 9: constants.DiskPhysicalType.FLASH, 99: constants.DiskPhysicalType.VMDISK } + VOLUME_PERF_METRICS = { + 'readIops': 'sp.*.storage.lun.*.readsRate', + 'writeIops': 'sp.*.storage.lun.*.writesRate', + 'readThroughput': 'sp.*.storage.lun.*.readBytesRate', + 'writeThroughput': 'sp.*.storage.lun.*.writeBytesRate', + 'responseTime': 'sp.*.storage.lun.*.responseTime', + 'readIoSize': 'sp.*.storage.lun.*.avgReadSize', + 'writeIoSize': 'sp.*.storage.lun.*.avgWriteSize' + } + DISK_PERF_METRICS = { + 'readIops': 'sp.*.physical.disk.*.readsRate', + 'writeIops': 'sp.*.physical.disk.*.writesRate', + 'readThroughput': 'sp.*.physical.disk.*.readBytesRate', + 'writeThroughput': 'sp.*.physical.disk.*.writeBytesRate', + 'responseTime': 'sp.*.physical.disk.*.responseTime' + } + ETHERNET_PORT_METRICS = { + 'readThroughput': 'sp.*.net.device.*.bytesInRate', + 'writeThroughput': 'sp.*.net.device.*.bytesOutRate', + 'readIops': 'sp.*.net.device.*.pktsInRate', + 'writeIops': 'sp.*.net.device.*.pktsOutRate', + } + FC_PORT_METRICS = { + 'readIops': 'sp.*.fibreChannel.fePort.*.readsRate', + 'writeIops': 'sp.*.fibreChannel.fePort.*.writesRate', + 'readThroughput': 'sp.*.fibreChannel.fePort.*.readBytesRate', + 'writeThroughput': 'sp.*.fibreChannel.fePort.*.writeBytesRate' + } + ISCSI_PORT_METRICS = { + 'readIops': 'sp.*.iscsi.fePort.*.readsRate', + 'writeIops': 'sp.*.iscsi.fePort.*.writesRate', + 'readThroughput': 'sp.*.iscsi.fePort.*.readBytesRate', + 'writeThroughput': 'sp.*.iscsi.fePort.*.writeBytesRate' + } + FILESYSTEM_PERF_METRICS = { + 'readIops': 'sp.*.storage.filesystem.*.readsRate', + 'writeIops': 'sp.*.storage.filesystem.*.writesRate', + 'readThroughput': 'sp.*.storage.filesystem.*.readBytesRate', + 'writeThroughput': 'sp.*.storage.filesystem.*.writeBytesRate', + 'readIoSize': 'sp.*.storage.filesystem.*.readSizeAvg', + 'writeIoSize': 'sp.*.storage.filesystem.*.writeSizeAvg' + } + PERF_TYPE_MAP = { + 'readIops': {'write': 'writeIops', + 'total': 'iops'}, + 'readThroughput': {'write': 'writeThroughput', + 'total': 'throughput'}, + 'readIoSize': {'write': 'writeIoSize', + 'total': 'ioSize'}, + } + MS_PER_HOUR = 60 * 60 * 1000 def __init__(self, **kwargs): super().__init__(**kwargs) @@ -82,48 +135,51 @@ def get_storage(self, context): system_info = self.rest_handler.get_storage() capacity = self.rest_handler.get_capacity() version_info = self.rest_handler.get_soft_version() - if system_info is not None and capacity is not None: - system_entries = system_info.get('entries') - for system in system_entries: - content = system.get('content', {}) - name = content.get('name') - model = content.get('model') - serial_number = content.get('serialNumber') - health_value = content.get('health', {}).get('value') - status = UnityStorDriver.STORAGE_STATUS_MAP.get( - health_value, constants.StorageStatus.ABNORMAL) - break - capacity_info = capacity.get('entries') - for per_capacity in capacity_info: - content = per_capacity.get('content', {}) - free = content.get('sizeFree') - total = content.get('sizeTotal') - used = content.get('sizeUsed') - subs = content.get('sizeSubscribed') - break - if version_info: - soft_version = version_info.get('entries') - for soft_info in soft_version: - content = soft_info.get('content', {}) - if content: - version = content.get('id') - break - raw_capacity = self.get_disk_capacity(context) - raw_capacity = raw_capacity if raw_capacity else int(total) - system_result = { - 'name': name, - 'vendor': 'DELL EMC', - 'model': model, - 'status': status, - 'serial_number': serial_number, - 'firmware_version': version, - 'location': '', - 'subscribed_capacity': int(subs), - 'total_capacity': int(total), - 'raw_capacity': raw_capacity, - 'used_capacity': int(used), - 'free_capacity': int(free) - } + if not system_info or not capacity: + err_msg = "unity get system or capacity info failed" + LOG.error(err_msg) + raise exception.StorageBackendException(err_msg) + system_entries = system_info.get('entries') + for system in system_entries: + content = system.get('content', {}) + name = content.get('name') + model = content.get('model') + serial_number = content.get('serialNumber') + health_value = content.get('health', {}).get('value') + status = UnityStorDriver.STORAGE_STATUS_MAP.get( + health_value, constants.StorageStatus.ABNORMAL) + break + capacity_info = capacity.get('entries') + for per_capacity in capacity_info: + content = per_capacity.get('content', {}) + free = content.get('sizeFree') + total = content.get('sizeTotal') + used = content.get('sizeUsed') + subs = content.get('sizeSubscribed') + break + if version_info: + soft_version = version_info.get('entries') + for soft_info in soft_version: + content = soft_info.get('content', {}) + if content: + version = content.get('id') + break + raw_capacity = self.get_disk_capacity(context) + raw_capacity = raw_capacity if raw_capacity else int(total) + system_result = { + 'name': name, + 'vendor': 'DELL EMC', + 'model': model, + 'status': status, + 'serial_number': serial_number, + 'firmware_version': version, + 'location': '', + 'subscribed_capacity': int(subs), + 'total_capacity': int(total), + 'raw_capacity': raw_capacity, + 'used_capacity': int(used), + 'free_capacity': int(free) + } return system_result def list_storage_pools(self, context): @@ -381,7 +437,7 @@ def list_disks(self, context): try: disks = self.rest_handler.get_all_disks() disk_list = [] - if disks is not None: + if disks and disks.get('entries'): disk_entries = disks.get('entries') for disk in disk_entries: content = disk.get('content') @@ -417,6 +473,8 @@ def list_disks(self, context): 'location': content.get('name') } disk_list.append(disk_result) + else: + disk_list = self.get_virtual_disk() return disk_list except Exception as err: @@ -650,3 +708,305 @@ def clear_alert(self, context, alert): @staticmethod def get_access_url(): return 'https://{ip}' + + def get_metrics_loop(self, target, start_time, + end_time, metrics, path): + page = 1 + bend = False + time_map = {'latest_time': 0} + if not path: + return + while True: + if bend is True: + break + results = self.rest_handler.get_history_metrics(path, page) + if not results: + break + if 'entries' not in results: + break + if len(results['entries']) < 1: + break + bend = UnityStorDriver.get_metric_value( + target, start_time, end_time, metrics, results, time_map) + page += 1 + + def get_history_metrics(self, resource_type, targets, + start_time, end_time): + metrics = [] + for target in targets: + path = None + if resource_type == constants.ResourceType.VOLUME: + path = self.VOLUME_PERF_METRICS.get(target) + elif resource_type == constants.ResourceType.DISK: + path = self.DISK_PERF_METRICS.get(target) + elif resource_type == constants.ResourceType.FILESYSTEM: + path = self.FILESYSTEM_PERF_METRICS.get(target) + elif resource_type == constants.ResourceType.PORT: + self.get_metrics_loop(target, start_time, end_time, metrics, + self.ETHERNET_PORT_METRICS.get(target)) + self.get_metrics_loop(target, start_time, end_time, metrics, + self.FC_PORT_METRICS.get(target)) + continue + if path: + self.get_metrics_loop(target, start_time, end_time, + metrics, path) + return metrics + + @staticmethod + def get_metric_value(target, start_time, end_time, metrics, + results, time_map): + try: + if results is None: + return True + entries = results.get('entries') + for entry in entries: + content = entry.get('content') + if not content or not content.get('values'): + continue + occur_time = int(time.mktime(time.strptime( + content.get('timestamp'), + AlertHandler.TIME_PATTERN)) + ) * AlertHandler.SECONDS_TO_MS + hour_offset = (time.mktime(time.localtime()) - time.mktime( + time.gmtime())) / AlertHandler.SECONDS_PER_HOUR + occur_time = occur_time + (int(hour_offset) * + UnityStorDriver.MS_PER_HOUR) + if occur_time < start_time: + return True + if time_map.get('latest_time') <= occur_time \ + and time_map.get('latest_time') != 0: + continue + time_map['latest_time'] = occur_time + if start_time <= occur_time <= end_time: + for sp_value in content.get('values'): + perf_value = content.get('values').get(sp_value) + for key, value in perf_value.items(): + bfind = False + value = float(value) + for metric in metrics: + if metric.get('resource_id') == key and \ + metric.get('type') == target: + if metric.get('values').get( + occur_time): + if target == 'responseTime': + metric.get( + 'values')[occur_time] = \ + max(value, metric.get( + 'values').get( + occur_time)) + else: + metric.get('values')[ + occur_time] += value + else: + metric.get('values')[occur_time] \ + = value + bfind = True + break + if bfind is False: + metric_value = { + 'type': target, + 'resource_id': key, + 'values': {occur_time: value} + } + metrics.append(metric_value) + except Exception as err: + err_msg = "Failed to collect history metrics from Unity: %s, " \ + "target:%s" % (six.text_type(err), target) + LOG.error(err_msg) + return False + + @staticmethod + def count_total_perf(metrics): + if metrics is None: + return + for metric in metrics: + write_tye = None + total_type = None + if UnityStorDriver.PERF_TYPE_MAP.get(metric.get('type')): + write_tye = UnityStorDriver.PERF_TYPE_MAP.get( + metric.get('type')).get('write') + total_type = UnityStorDriver.PERF_TYPE_MAP.get( + metric.get('type')).get('total') + else: + continue + for metric_write in metrics: + if metric_write.get('resource_id') == \ + metric.get('resource_id') \ + and metric_write.get('type') == write_tye: + total = { + 'type': total_type, + 'resource_id': metric.get('resource_id') + } + bfind_total = False + for tr, read in metric.get('values').items(): + for tw, write in metric_write.get( + 'values').items(): + if tr == tw: + value = read + write + if total.get('values'): + total['values'][tr] = value + else: + total['values'] = {tr: value} + bfind_total = True + break + if bfind_total: + metrics.append(total) + break + + @staticmethod + def package_metrics(storage_id, resource_type, metrics, metrics_list): + for metric in metrics_list: + unit = None + if resource_type == constants.ResourceType.PORT: + unit = consts.PORT_CAP[metric.get('type')]['unit'] + elif resource_type == constants.ResourceType.VOLUME: + unit = consts.VOLUME_CAP[metric.get('type')]['unit'] + elif resource_type == constants.ResourceType.DISK: + unit = consts.DISK_CAP[metric.get('type')]['unit'] + elif resource_type == constants.ResourceType.FILESYSTEM: + unit = consts.FILESYSTEM_CAP[metric.get('type')]['unit'] + labels = { + 'storage_id': storage_id, + 'resource_type': resource_type, + 'resource_id': metric.get('resource_id'), + 'type': 'RAW', + 'unit': unit + } + if 'THROUGHPUT' in metric.get('type').upper() or \ + 'RESPONSETIME' in metric.get('type').upper(): + for tm in metric.get('values'): + metric['values'][tm] = metric['values'][tm] / units.k + value = constants.metric_struct(name=metric.get('type'), + labels=labels, + values=metric.get('values')) + metrics.append(value) + + def collect_perf_metrics(self, context, storage_id, + resource_metrics, start_time, + end_time): + metrics = [] + try: + if resource_metrics.get(constants.ResourceType.VOLUME): + volume_metrics = self.get_history_metrics( + constants.ResourceType.VOLUME, + resource_metrics.get(constants.ResourceType.VOLUME), + start_time, + end_time) + UnityStorDriver.count_total_perf(volume_metrics) + UnityStorDriver.package_metrics(storage_id, + constants.ResourceType.VOLUME, + metrics, volume_metrics) + if resource_metrics.get(constants.ResourceType.DISK): + disk_metrics = self.get_history_metrics( + constants.ResourceType.DISK, + resource_metrics.get(constants.ResourceType.DISK), + start_time, + end_time) + UnityStorDriver.count_total_perf(disk_metrics) + UnityStorDriver.package_metrics(storage_id, + constants.ResourceType.DISK, + metrics, disk_metrics) + if resource_metrics.get(constants.ResourceType.PORT): + port_metrics = self.get_history_metrics( + constants.ResourceType.PORT, + resource_metrics.get(constants.ResourceType.PORT), + start_time, + end_time) + UnityStorDriver.count_total_perf(port_metrics) + UnityStorDriver.package_metrics(storage_id, + constants.ResourceType.PORT, + metrics, port_metrics) + if resource_metrics.get(constants.ResourceType.FILESYSTEM): + file_metrics = self.get_history_metrics( + constants.ResourceType.FILESYSTEM, + resource_metrics.get(constants.ResourceType.FILESYSTEM), + start_time, + end_time) + UnityStorDriver.count_total_perf(file_metrics) + UnityStorDriver.package_metrics( + storage_id, constants.ResourceType.FILESYSTEM, + metrics, file_metrics) + except Exception as err: + err_msg = "Failed to collect metrics from Unity: %s" % \ + (six.text_type(err)) + LOG.error(err_msg) + raise exception.InvalidResults(err_msg) + return metrics + + @staticmethod + def get_capabilities(context, filters=None): + """Get capability of supported driver""" + return { + 'is_historic': True, + 'resource_metrics': { + constants.ResourceType.VOLUME: consts.VOLUME_CAP, + constants.ResourceType.PORT: consts.PORT_CAP, + constants.ResourceType.DISK: consts.DISK_CAP, + constants.ResourceType.FILESYSTEM: consts.FILESYSTEM_CAP + } + } + + def get_latest_perf_timestamp(self, context): + latest_time = 0 + page = 1 + results = self.rest_handler.get_history_metrics( + UnityStorDriver.VOLUME_PERF_METRICS.get('readIops'), page) + if not results: + results = self.rest_handler.get_history_metrics( + UnityStorDriver.ETHERNET_PORT_METRICS.get('readIops'), page) + if results: + if 'entries' in results: + entries = results.get('entries') + for entry in entries: + content = entry.get('content') + if not content: + continue + occur_time = int(time.mktime(time.strptime( + content.get('timestamp'), + AlertHandler.TIME_PATTERN)) + ) * AlertHandler.SECONDS_TO_MS + hour_offset = \ + (time.mktime(time.localtime()) - + time.mktime(time.gmtime()))\ + / AlertHandler.SECONDS_PER_HOUR + occur_time = occur_time + (int(hour_offset) * + UnityStorDriver.MS_PER_HOUR) + latest_time = occur_time + break + return latest_time + + def get_virtual_disk(self): + try: + disks = self.rest_handler.get_virtual_disks() + disk_list = [] + if disks is not None: + disk_entries = disks.get('entries') + for disk in disk_entries: + content = disk.get('content') + if not content: + continue + health_value = content.get('health', {}).get('value') + slot_info = \ + content.get('health', {}).get('descriptionIds', []) + if 'ALRT_DISK_SLOT_EMPTY' in slot_info: + continue + if health_value in UnityStorDriver.HEALTH_OK: + status = constants.DiskStatus.NORMAL + else: + status = constants.DiskStatus.ABNORMAL + disk_result = { + 'name': content.get('name'), + 'storage_id': self.storage_id, + 'native_disk_id': content.get('id'), + 'capacity': int(content.get('sizeTotal')), + 'status': status, + 'manufacturer': content.get('manufacturer'), + 'model': content.get('model'), + 'physical_type': constants.DiskPhysicalType.VMDISK + } + disk_list.append(disk_result) + return disk_list + except Exception as err: + err_msg = "Failed to get virtual disk from Unity: %s" % \ + (six.text_type(err)) + raise exception.InvalidResults(err_msg) 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 a4fc5d3c5..aa0901481 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 @@ -1323,6 +1323,928 @@ 'user_group_name': '22222' } ] +GET_ETH_PORT_READ_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "spa_eth0": "10000", + "spa_eth1": "20000", + "spa_eth2": "30000", + "spa_eth3": "40000" + }, + "spb": { + "spa_eth0": "10000", + "spa_eth1": "20000", + "spa_eth2": "30000", + "spa_eth3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spa": { + "spa_eth0": "40000", + "spa_eth1": "30000", + "spa_eth2": "20000", + "spa_eth3": "10000" + }, + "spb": { + "spa_eth0": "40000", + "spa_eth1": "30000", + "spa_eth2": "20000", + "spa_eth3": "10000" + } + } + } + } + ] +} +GET_ETH_PORT_READ_THR_PERF_NULL = { + "entries": [] +} +GET_ETH_PORT_WRITE_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "spa_eth0": "90000", + "spa_eth1": "80000", + "spa_eth2": "70000", + "spa_eth3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spa": { + "spa_eth0": "60000", + "spa_eth1": "70000", + "spa_eth2": "80000", + "spa_eth3": "90000" + } + } + } + } + ] +} +GET_ETH_PORT_WRITE_THR_PERF_NULL = { + "entries": [] +} +GET_FC_PORT_READ_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "spa_fc0": "10000", + "spa_fc1": "20000", + "spa_fc2": "30000", + "spa_fc3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spa": { + "spa_fc0": "40000", + "spa_fc1": "30000", + "spa_fc2": "20000", + "spa_fc3": "10000" + } + } + } + } + ] +} +GET_FC_PORT_READ_THR_PERF_NULL = { + "entries": [] +} +GET_FC_PORT_WRITE_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "spa_fc0": "90000", + "spa_fc1": "80000", + "spa_fc2": "70000", + "spa_fc3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spa": { + "spa_fc0": "60000", + "spa_fc1": "70000", + "spa_fc2": "80000", + "spa_fc3": "90000" + } + } + } + } + ] +} +GET_FC_PORT_WRITE_THR_PERF_NULL = { + "entries": [] +} +GET_FC_PORT_READ_IOPS_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "spa_fc0": "10000", + "spa_fc1": "20000", + "spa_fc2": "30000", + "spa_fc3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spa": { + "spa_fc0": "40000", + "spa_fc1": "30000", + "spa_fc2": "20000", + "spa_fc3": "10000" + } + } + } + } + ] +} +GET_FC_PORT_READ_IOPS_PERF_NULL = { + "entries": [] +} +GET_FC_PORT_WRITE_IOPS_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "spa_fc0": "90000", + "spa_fc1": "80000", + "spa_fc2": "70000", + "spa_fc3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "spa_fc0": "60000", + "spa_fc1": "70000", + "spa_fc2": "80000", + "spa_fc3": "90000" + } + } + } + } + ] +} +GET_FC_PORT_WRITE_IOPS_PERF_NULL = { + "entries": [] +} +GET_VOLUME_READ_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "volume0": "10000", + "volume1": "20000", + "volume2": "30000", + "volume3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "volume0": "40000", + "volume1": "30000", + "volume2": "20000", + "volume3": "10000" + } + } + } + } + ] +} +GET_VOLUME_READ_THR_PERF_NULL = { + "entries": [] +} +GET_VOLUME_WRITE_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "volume0": "90000", + "volume1": "80000", + "volume2": "70000", + "volume3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "volume0": "60000", + "volume1": "70000", + "volume2": "80000", + "volume3": "90000" + } + } + } + } + ] +} +GET_VOLUME_WRITE_THR_PERF_NULL = { + "entries": [] +} +GET_VOLUME_READ_IOPS_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "volume0": "10000", + "volume1": "20000", + "volume2": "30000", + "volume3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "volume0": "40000", + "volume1": "30000", + "volume2": "20000", + "volume3": "10000" + } + } + } + } + ] +} +GET_VOLUME_READ_IOPS_PERF_NULL = { + "entries": [] +} +GET_VOLUME_WRITE_IOPS_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "volume0": "90000", + "volume1": "80000", + "volume2": "70000", + "volume3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "volume0": "60000", + "volume1": "70000", + "volume2": "80000", + "volume3": "90000" + } + } + } + } + ] +} +GET_VOLUME_WRITE_IOPS_PERF_NULL = { + "entries": [] +} +GET_VOLUME_READ_IO_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "volume0": "10000", + "volume1": "20000", + "volume2": "30000", + "volume3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "volume0": "40000", + "volume1": "30000", + "volume2": "20000", + "volume3": "10000" + } + } + } + } + ] +} +GET_VOLUME_READ_IO_PERF_NULL = { + "entries": [] +} +GET_VOLUME_WRITE_IO_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "volume0": "90000", + "volume1": "80000", + "volume2": "70000", + "volume3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "volume0": "60000", + "volume1": "70000", + "volume2": "80000", + "volume3": "90000" + } + } + } + } + ] +} +GET_VOLUME_WRITE_IO_PERF_NULL = { + "entries": [] +} +GET_VOLUME_RESPONSE_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "volume0": "90000", + "volume1": "80000", + "volume2": "70000", + "volume3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "volume0": "60000", + "volume1": "70000", + "volume2": "80000", + "volume3": "90000" + } + } + } + } + ] +} +GET_VOLUME_RESPONSE_PERF_NULL = { + "entries": [] +} +GET_DISK_READ_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "DISK0": "10000", + "DISK1": "20000", + "DISK2": "30000", + "DISK3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "DISK0": "40000", + "DISK1": "30000", + "DISK2": "20000", + "DISK3": "10000" + } + } + } + } + ] +} +GET_DISK_READ_THR_PERF_NULL = { + "entries": [] +} +GET_DISK_WRITE_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "DISK0": "90000", + "DISK1": "80000", + "DISK2": "70000", + "DISK3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "DISK0": "60000", + "DISK1": "70000", + "DISK2": "80000", + "DISK3": "90000" + } + } + } + } + ] +} +GET_DISK_WRITE_THR_PERF_NULL = { + "entries": [] +} +GET_DISK_READ_IOPS_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "DISK0": "10000", + "DISK1": "20000", + "DISK2": "30000", + "DISK3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "DISK0": "40000", + "DISK1": "30000", + "DISK2": "20000", + "DISK3": "10000" + } + } + } + } + ] +} +GET_DISK_READ_IOPS_PERF_NULL = { + "entries": [] +} +GET_DISK_WRITE_IOPS_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "DISK0": "90000", + "DISK1": "80000", + "DISK2": "70000", + "DISK3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "DISK0": "60000", + "DISK1": "70000", + "DISK2": "80000", + "DISK3": "90000" + } + } + } + } + ] +} +GET_DISK_WRITE_IOPS_PERF_NULL = { + "entries": [] +} +GET_DISK_RESPONSE_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "DISK0": "90000", + "DISK1": "80000", + "DISK2": "70000", + "DISK3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "DISK0": "60000", + "DISK1": "70000", + "DISK2": "80000", + "DISK3": "90000" + } + } + } + } + ] +} +GET_DISK_RESPONSE_PERF_NULL = { + "entries": [] +} +GET_FILE_READ_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "FILE0": "10000", + "FILE1": "20000", + "FILE2": "30000", + "FILE3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "FILE0": "40000", + "FILE1": "30000", + "FILE2": "20000", + "FILE3": "10000" + } + } + } + } + ] +} +GET_FILE_READ_THR_PERF_NULL = { + "entries": [] +} +GET_FILE_WRITE_THR_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "FILE0": "90000", + "FILE1": "80000", + "FILE2": "70000", + "FILE3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "FILE0": "60000", + "FILE1": "70000", + "FILE2": "80000", + "FILE3": "90000" + } + } + } + } + ] +} +GET_FILE_WRITE_THR_PERF_NULL = { + "entries": [] +} +GET_FILE_READ_IOPS_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "FILE0": "10000", + "FILE1": "20000", + "FILE2": "30000", + "FILE3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "FILE0": "40000", + "FILE1": "30000", + "FILE2": "20000", + "FILE3": "10000" + } + } + } + } + ] +} +GET_FILE_READ_IOPS_PERF_NULL = { + "entries": [] +} +GET_FILE_WRITE_IOPS_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "FILE0": "90000", + "FILE1": "80000", + "FILE2": "70000", + "FILE3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "FILE0": "60000", + "FILE1": "70000", + "FILE2": "80000", + "FILE3": "90000" + } + } + } + } + ] +} +GET_FILE_WRITE_IOPS_PERF_NULL = { + "entries": [] +} +GET_FILE_READ_IO_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "FILE0": "10000", + "FILE1": "20000", + "FILE2": "30000", + "FILE3": "40000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "FILE0": "40000", + "FILE1": "30000", + "FILE2": "20000", + "FILE3": "10000" + } + } + } + } + ] +} +GET_FILE_READ_IO_PERF_NULL = { + "entries": [] +} +GET_FILE_WRITE_IO_PERF = { + "entries": [ + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:47:10.000Z", + "values": { + "spa": { + "FILE0": "90000", + "FILE1": "80000", + "FILE2": "70000", + "FILE3": "60000" + } + } + } + }, + { + "content": { + "queryId": 46, + "path": "sp.*.net.device.*.bytesOut", + "timestamp": "2021-07-08T06:46:10.000Z", + "values": { + "spb": { + "FILE0": "60000", + "FILE1": "70000", + "FILE2": "80000", + "FILE3": "90000" + } + } + } + } + ] +} +GET_FILE_WRITE_IO_PERF_NULL = { + "entries": [] +} +resource_metrics = { + 'volume': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput', + 'responseTime', + 'ioSize', 'readIoSize', 'writeIoSize', + ], + 'port': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput' + ], + 'disk': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput', + 'responseTime' + ], + 'filesystem': [ + 'iops', 'readIops', 'writeIops', + 'throughput', 'readThroughput', 'writeThroughput', + 'readIoSize', 'writeIoSize' + ] +} class TestUNITYStorDriver(TestCase): @@ -1472,3 +2394,74 @@ def test_list_quotas(self, mock_user, mock_qtree): mock_qtree.return_value = GET_ALL_QTREE quota = UnityStorDriver(**ACCESS_INFO).list_quotas(context) self.assertEqual(quota, quota_result) + + @mock.patch.object(RestHandler, 'get_history_metrics') + def test_collect_perf_metrics(self, mock_history): + RestHandler.login = mock.Mock(return_value=None) + start_time = 1625726770000 + end_time = 1625726830000 + storage_id = '12345' + mock_history.side_effect = [GET_VOLUME_READ_THR_PERF, + GET_VOLUME_READ_THR_PERF_NULL, + GET_VOLUME_WRITE_THR_PERF, + GET_VOLUME_WRITE_THR_PERF_NULL, + GET_VOLUME_READ_IOPS_PERF, + GET_VOLUME_READ_IOPS_PERF_NULL, + GET_VOLUME_WRITE_IOPS_PERF, + GET_VOLUME_WRITE_IOPS_PERF_NULL, + GET_VOLUME_READ_IO_PERF, + GET_VOLUME_READ_IO_PERF_NULL, + GET_VOLUME_WRITE_IO_PERF, + GET_VOLUME_WRITE_IO_PERF_NULL, + GET_VOLUME_RESPONSE_PERF, + GET_VOLUME_RESPONSE_PERF_NULL, + GET_DISK_READ_THR_PERF, + GET_DISK_READ_THR_PERF_NULL, + GET_DISK_WRITE_THR_PERF, + GET_DISK_WRITE_THR_PERF_NULL, + GET_DISK_READ_IOPS_PERF, + GET_DISK_READ_IOPS_PERF_NULL, + GET_DISK_WRITE_IOPS_PERF, + GET_DISK_WRITE_IOPS_PERF_NULL, + GET_DISK_RESPONSE_PERF, + GET_DISK_RESPONSE_PERF_NULL, + GET_ETH_PORT_READ_THR_PERF, + GET_ETH_PORT_READ_THR_PERF_NULL, + GET_ETH_PORT_WRITE_THR_PERF, + GET_ETH_PORT_WRITE_THR_PERF_NULL, + GET_FC_PORT_READ_THR_PERF, + GET_ETH_PORT_READ_THR_PERF, + GET_ETH_PORT_READ_THR_PERF_NULL, + GET_ETH_PORT_WRITE_THR_PERF, + GET_ETH_PORT_WRITE_THR_PERF_NULL, + GET_FC_PORT_READ_THR_PERF, + GET_FC_PORT_READ_THR_PERF_NULL, + GET_FC_PORT_WRITE_THR_PERF, + GET_FC_PORT_WRITE_THR_PERF_NULL, + GET_FC_PORT_READ_IOPS_PERF, + GET_FC_PORT_READ_IOPS_PERF_NULL, + GET_FC_PORT_WRITE_IOPS_PERF, + GET_FC_PORT_WRITE_IOPS_PERF_NULL, + GET_FILE_READ_THR_PERF, + GET_FILE_READ_THR_PERF_NULL, + GET_FILE_WRITE_THR_PERF, + GET_FILE_WRITE_THR_PERF_NULL, + GET_FILE_READ_IOPS_PERF, + GET_FILE_READ_IOPS_PERF_NULL, + GET_FILE_WRITE_IOPS_PERF, + GET_FILE_WRITE_IOPS_PERF_NULL, + GET_FILE_READ_IO_PERF, + GET_FILE_READ_IO_PERF_NULL, + GET_FILE_WRITE_IO_PERF, + GET_FILE_WRITE_IO_PERF_NULL] + metrics = UnityStorDriver(**ACCESS_INFO).collect_perf_metrics( + context, storage_id, resource_metrics, start_time, end_time) + self.assertEqual(metrics[0][1]['resource_id'], 'volume0') + + @mock.patch.object(RestHandler, 'get_history_metrics') + def test_latest_perf_timestamp(self, mock_history): + RestHandler.login = mock.Mock(return_value=None) + mock_history.return_value = GET_VOLUME_READ_THR_PERF + last_time = UnityStorDriver(**ACCESS_INFO).get_latest_perf_timestamp( + context) + self.assertEqual(last_time, 1625726830000) From 21afd81221f98ce43b9e9c8de137a82a35d95d8c Mon Sep 17 00:00:00 2001 From: guankc <80501304+guankc@users.noreply.github.com> Date: Wed, 18 May 2022 16:21:04 +0800 Subject: [PATCH 16/17] add NetApp mapping view interface (#797) --- .../drivers/netapp/dataontap/cluster_mode.py | 13 + delfin/drivers/netapp/dataontap/constants.py | 27 ++ .../netapp/dataontap/mapping_handler.py | 247 +++++++++++++++++ .../netapp/dataontap/netapp_handler.py | 88 ++++++ delfin/drivers/utils/tools.py | 5 +- .../netapp/netapp_ontap/test_constans.py | 258 ++++++++++++++++++ .../netapp/netapp_ontap/test_netapp.py | 29 ++ 7 files changed, 665 insertions(+), 2 deletions(-) create mode 100644 delfin/drivers/netapp/dataontap/mapping_handler.py diff --git a/delfin/drivers/netapp/dataontap/cluster_mode.py b/delfin/drivers/netapp/dataontap/cluster_mode.py index bf431b4d5..91295f115 100644 --- a/delfin/drivers/netapp/dataontap/cluster_mode.py +++ b/delfin/drivers/netapp/dataontap/cluster_mode.py @@ -91,3 +91,16 @@ def get_capabilities(context, filters=None): def get_latest_perf_timestamp(self, context): return self.netapp_handler.get_latest_perf_timestamp() + + def list_storage_host_initiators(self, context): + return self.netapp_handler.\ + list_storage_host_initiators(self.storage_id) + + def list_port_groups(self, context): + return self.netapp_handler.list_port_groups(self.storage_id) + + def list_masking_views(self, context): + return self.netapp_handler.list_masking_views(self.storage_id) + + def list_storage_hosts(self, context): + return self.netapp_handler.list_storage_hosts(self.storage_id) diff --git a/delfin/drivers/netapp/dataontap/constants.py b/delfin/drivers/netapp/dataontap/constants.py index 0a33603a2..567ac35de 100644 --- a/delfin/drivers/netapp/dataontap/constants.py +++ b/delfin/drivers/netapp/dataontap/constants.py @@ -67,6 +67,9 @@ FLOAT_PATTERN = r"\d\.\d" IP_PATTERN = re.compile(r'(([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])\.){3}' r'([01]{0,1}\d{0,1}\d|2[0-4]\d|25[0-5])$') +IQN_PATTERN = re.compile('^[i][q][n].') +WWN_PATTERN = re.compile('^(([a-z|0-9]){2}:){7}(([a-z|0-9]){2})') +INITIATOR_KEY = 'loggedin' CLUSTER_SHOW_COMMAND = "cluster identity show" VERSION_SHOW_COMMAND = "version" STORAGE_STATUS_COMMAND = "system health status show" @@ -118,6 +121,17 @@ CONTROLLER_IP_COMMAND = "network interface show -fields " \ "curr-node,address -role node-mgmt" +HOST_COMMAND = "igroup show -instance" + +PORT_GROUP_COMMAND = "portset show -instance" +LIF_COMMAND = "network interface show -instance" + +FC_INITIATOR_COMMAND = "fcp initiator show -instance" + +ISCSI_INITIATOR_COMMAND = "iscsi initiator show -instance" + +LUN_MAPPING_COMMAND = 'lun mapping show -instance' + SECURITY_STYLE = { 'mixed': constants.NASSecurityMode.MIXED, 'ntfs': constants.NASSecurityMode.NTFS, @@ -640,3 +654,16 @@ "readIops": READ_IOPS_DESCRIPTION, "writeIops": WRITE_IOPS_DESCRIPTION, } + +HOST_OS_TYPE_MAP = { + 'solaris': constants.HostOSTypes.SOLARIS, + 'windows': constants.HostOSTypes.WINDOWS, + 'hpux': constants.HostOSTypes.HP_UX, + 'aix': constants.HostOSTypes.AIX, + 'linux': constants.HostOSTypes.LINUX, + 'netware': constants.HostOSTypes.UNKNOWN, + 'vmware': constants.HostOSTypes.VMWARE_ESX, + 'openvms': constants.HostOSTypes.OPEN_VMS, + 'xen': constants.HostOSTypes.XEN_SERVER, + 'hyper_v': constants.HostOSTypes.UNKNOWN +} diff --git a/delfin/drivers/netapp/dataontap/mapping_handler.py b/delfin/drivers/netapp/dataontap/mapping_handler.py new file mode 100644 index 000000000..3308f5a9d --- /dev/null +++ b/delfin/drivers/netapp/dataontap/mapping_handler.py @@ -0,0 +1,247 @@ +# Copyright 2021 The SODA Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from delfin.common import constants +from delfin.drivers.netapp.dataontap import constants as constant +from delfin.drivers.utils.tools import Tools + + +class MappingHandler(object): + + @staticmethod + def format_initiators(initiator_list, initiator_info, storage_id, + protocol_type, is_default=False): + initiator_map_list = [] + Tools.split_value_map_list( + initiator_info, initiator_map_list, is_mapping=True, split=':') + if not is_default and protocol_type ==\ + constants.InitiatorType.FC: + MappingHandler.get_fc_initiator( + initiator_list, initiator_map_list, storage_id) + elif not is_default and protocol_type ==\ + constants.InitiatorType.ISCSI: + MappingHandler.get_iscsi_initiator( + initiator_list, initiator_map_list, storage_id) + if is_default: + MappingHandler.get_initiator_from_host( + initiator_list, initiator_map_list, storage_id) + return initiator_list + + @staticmethod + def duplicate_removal(initiator_list, initiator_model): + is_same = False + for initiator in initiator_list: + if initiator['native_storage_host_initiator_id'] \ + == initiator_model['native_storage_host_initiator_id']: + is_same = True + break + if not is_same: + initiator_list.append(initiator_model) + + @staticmethod + def get_iscsi_initiator(initiator_list, initiator_map_list, storage_id): + for initiator_map in initiator_map_list: + if 'IgroupName' in initiator_map \ + and initiator_map.get('IgroupName') == '-': + initiator_id = \ + initiator_map.get('InitiatorName').replace(' ', '') + initiator_model = { + 'native_storage_host_initiator_id': initiator_id, + 'native_storage_host_id': None, + 'name': initiator_id, + 'alias': initiator_map.get('InitiatorAlias'), + 'type': constants.InitiatorType.ISCSI, + 'status': constants.InitiatorStatus.ONLINE, + 'wwn': initiator_id, + 'storage_id': storage_id, + } + MappingHandler.duplicate_removal( + initiator_list, initiator_model) + + @staticmethod + def get_fc_initiator(initiator_list, initiator_map_list, storage_id): + for initiator_map in initiator_map_list: + if 'IgroupName' in initiator_map \ + and initiator_map.get('IgroupName') == '-': + initiator_id = \ + initiator_map.get('InitiatorWWPN').replace(' ', '') + initiator_model = { + 'native_storage_host_initiator_id': initiator_id, + 'native_storage_host_id': None, + 'name': initiator_id, + 'alias': initiator_map.get('InitiatorWWPNAlias'), + 'type': constants.InitiatorType.FC, + 'status': constants.InitiatorStatus.ONLINE, + 'wwn': initiator_map.get('InitiatorWWPN'), + 'storage_id': storage_id, + } + MappingHandler.duplicate_removal( + initiator_list, initiator_model) + + @staticmethod + def get_initiator_type(protocol_type, initiator_name): + if protocol_type != 'mixed': + return constant.NETWORK_PORT_TYPE.get(protocol_type) + else: + if constant.IQN_PATTERN.search(initiator_name): + return constants.PortType.ISCSI + elif constant.WWN_PATTERN.search(initiator_name): + return constants.PortType.FC + return None + + @staticmethod + def format_initiator(data_map, initiator_id, storage_id): + initiator_id = initiator_id.split('(')[0] + protocol_type = \ + MappingHandler.get_initiator_type( + data_map.get('Protocol'), initiator_id) + host_id = '%s_%s' % (data_map.get('VserverName'), + data_map.get('IgroupName')) + initiator_model = { + 'native_storage_host_initiator_id': initiator_id, + 'native_storage_host_id': host_id, + 'name': initiator_id, + 'type': protocol_type, + 'status': constants.InitiatorStatus.ONLINE, + 'storage_id': storage_id, + 'wwn': initiator_id + } + return initiator_model + + @staticmethod + def get_initiator_from_host( + initiator_list, initiator_map_list, storage_id): + for initiator_map in initiator_map_list: + if 'IgroupName' in initiator_map: + initiator_id = \ + initiator_map.get('Initiators').replace(' ', '') + if initiator_map.get('Initiators') != '-': + initiator_list.append( + MappingHandler.format_initiator( + initiator_map, initiator_id, storage_id)) + for key in initiator_map: + if constant.INITIATOR_KEY in key and key != '-': + initiator_list.append( + MappingHandler.format_initiator( + initiator_map, key, storage_id)) + + @staticmethod + def format_host(initiator_info, storage_id): + initiator_map_list, initiator_list = [], [] + Tools.split_value_map_list(initiator_info, + initiator_map_list, + split=':') + for initiator_map in initiator_map_list: + if 'IgroupName' in initiator_map: + host_id = '%s_%s' % (initiator_map.get('VserverName'), + initiator_map.get('IgroupName')) + initiator_model = { + 'native_storage_host_id': host_id, + 'name': initiator_map.get('IgroupName'), + 'os_type': + constant.HOST_OS_TYPE_MAP.get( + initiator_map.get('OSType')), + 'status': constants.HostStatus.NORMAL, + 'storage_id': storage_id, + } + initiator_list.append(initiator_model) + return initiator_list + + @staticmethod + def format_port_group(port_set_info, lif_info, storage_id): + port_map_list, port_group_list = [], [] + lif_map_list, port_group_relation_list = [], [] + Tools.split_value_map_list(port_set_info, port_map_list, split=':') + Tools.split_value_map_list(lif_info, lif_map_list, split=':') + for port_map in port_map_list: + if 'PortsetName' in port_map: + port_group_id = "%s-%s-%s" % \ + (port_map.get('VserverName'), + port_map.get('PortsetName'), + port_map.get('Protocol')) + ports = \ + port_map.get('LIFOrTPGName').replace(' ', '').split(',') + ports_str = '' + for lif_map in lif_map_list: + if 'LogicalInterfaceName' in lif_map: + if lif_map.get('LogicalInterfaceName') in ports: + port_id = "%s_%s" % \ + (lif_map['CurrentNode'], + lif_map['CurrentPort']) + port_group_relation = { + 'storage_id': storage_id, + 'native_port_group_id': port_group_id, + 'native_port_id': port_id + } + port_group_relation_list.append( + port_group_relation) + if ports_str: + ports_str = \ + "{0},{1}".format(ports_str, port_id) + else: + ports_str = "{0}".format(port_id) + + port_group_model = { + 'native_port_group_id': port_group_id, + 'name': port_map.get('PortsetName'), + 'ports': ports_str, + 'storage_id': storage_id, + } + port_group_list.append(port_group_model) + result = { + 'port_groups': port_group_list, + 'port_grp_port_rels': port_group_relation_list + } + return result + + @staticmethod + def format_mapping_view(mapping_info, volume_info, storage_id, host_list): + mapping_map_list, mapping_view_list, volume_map_list = [], [], [] + Tools.split_value_map_list(mapping_info, mapping_map_list, split=":") + Tools.split_value_map_list(volume_info, volume_map_list, split=":") + for mapping_map in mapping_map_list: + if 'LUNPath' in mapping_map: + host_id = '%s_%s' % (mapping_map.get('VserverName'), + mapping_map.get('IgroupName')) + native_masking_view_id = \ + '%s_%s_%s_%s' % (mapping_map.get('LUNNode'), + mapping_map.get('VserverName'), + mapping_map.get('IgroupName'), + mapping_map.get('LUNName')) + name = '%s_%s' % (mapping_map.get('IgroupName'), + mapping_map.get('LUNName')) + port_group_id = "%s-%s-%s" % \ + (mapping_map.get('VserverName'), + mapping_map.get('PortsetBindingIgroup'), + mapping_map.get('IgroupProtocolType')) + native_volume_id = None + for volume_map in volume_map_list: + if 'LUNName' in volume_map: + if volume_map.get('LUNName') == \ + mapping_map.get('LUNName') \ + and volume_map.get('VserverName') == \ + mapping_map.get('VserverName') \ + and volume_map.get('LUNPath') == \ + mapping_map.get('LUNPath'): + native_volume_id = volume_map['SerialNumber'] + mapping_view = { + 'native_masking_view_id': + native_masking_view_id, + 'name': name, + 'native_port_group_id': port_group_id, + 'native_storage_host_id': host_id, + 'native_volume_id': native_volume_id, + 'storage_id': storage_id, + } + mapping_view_list.append(mapping_view) + return mapping_view_list diff --git a/delfin/drivers/netapp/dataontap/netapp_handler.py b/delfin/drivers/netapp/dataontap/netapp_handler.py index ad4075c8c..64bf25f70 100644 --- a/delfin/drivers/netapp/dataontap/netapp_handler.py +++ b/delfin/drivers/netapp/dataontap/netapp_handler.py @@ -26,6 +26,7 @@ from delfin.drivers.netapp.dataontap import constants as constant from delfin import exception, utils from delfin.common import constants +from delfin.drivers.netapp.dataontap.mapping_handler import MappingHandler from delfin.drivers.netapp.dataontap.performance_handler \ import PerformanceHandler from delfin.drivers.utils.rest_client import RestClient @@ -1279,3 +1280,90 @@ def get_latest_perf_timestamp(self): "netapp cmode: %s" % (six.text_type(err)) LOG.error(err_msg) raise exception.InvalidResults(err_msg) + + def list_storage_host_initiators(self, storage_id): + try: + initiator_list = [] + iscsi_initiator_info = self.ssh_pool.do_exec( + constant.ISCSI_INITIATOR_COMMAND) + fc_initiator_info = self.ssh_pool.do_exec( + constant.FC_INITIATOR_COMMAND) + new_initiator_info = self.ssh_pool.do_exec( + constant.HOST_COMMAND) + MappingHandler.format_initiators( + initiator_list, new_initiator_info, + storage_id, '', is_default=True) + MappingHandler.format_initiators( + initiator_list, iscsi_initiator_info, + storage_id, constants.InitiatorType.ISCSI) + MappingHandler.format_initiators( + initiator_list, fc_initiator_info, + storage_id, constants.InitiatorType.FC) + return initiator_list + except exception.DelfinException as e: + err_msg = "Failed to get storage initiators from " \ + "netapp cmode: %s" % (six.text_type(e)) + LOG.error(err_msg) + raise e + except Exception as err: + err_msg = "Failed to get storage initiators from " \ + "netapp cmode: %s" % (six.text_type(err)) + LOG.error(err_msg) + raise exception.InvalidResults(err_msg) + + def list_storage_hosts(self, storage_id): + try: + host_info = self.ssh_pool.do_exec(constant.HOST_COMMAND) + return MappingHandler.format_host(host_info, storage_id) + except exception.DelfinException as e: + err_msg = "Failed to get storage port groups from " \ + "netapp cmode: %s" % (six.text_type(e)) + LOG.error(err_msg) + raise e + except Exception as err: + err_msg = "Failed to get storage por groups from " \ + "netapp cmode: %s" % (six.text_type(err)) + LOG.error(err_msg) + raise exception.InvalidResults(err_msg) + + def list_port_groups(self, storage_id): + try: + port_set_info = self.ssh_pool.do_exec( + constant.PORT_GROUP_COMMAND) + lif_info = self.ssh_pool.do_exec( + constant.LIF_COMMAND) + return MappingHandler.format_port_group(port_set_info, + lif_info, + storage_id) + except exception.DelfinException as e: + err_msg = "Failed to get storage port groups from " \ + "netapp cmode: %s" % (six.text_type(e)) + LOG.error(err_msg) + raise e + except Exception as err: + err_msg = "Failed to get storage por groups from " \ + "netapp cmode: %s" % (six.text_type(err)) + LOG.error(err_msg) + raise exception.InvalidResults(err_msg) + + def list_masking_views(self, storage_id): + try: + mapping_info = self.ssh_pool.do_exec( + constant.LUN_MAPPING_COMMAND) + volume_info = self.ssh_pool.do_exec( + constant.LUN_SHOW_DETAIL_COMMAND) + host_list = self.list_storage_hosts(None) + return MappingHandler.format_mapping_view(mapping_info, + volume_info, + storage_id, + host_list) + except exception.DelfinException as e: + err_msg = "Failed to get storage masking views from " \ + "netapp cmode: %s" % (six.text_type(e)) + LOG.error(err_msg) + raise e + except Exception as err: + err_msg = "Failed to get storage masking views from " \ + "netapp cmode: %s" % (six.text_type(err)) + LOG.error(err_msg) + raise exception.InvalidResults(err_msg) diff --git a/delfin/drivers/utils/tools.py b/delfin/drivers/utils/tools.py index 969d285cf..7558f0dfa 100644 --- a/delfin/drivers/utils/tools.py +++ b/delfin/drivers/utils/tools.py @@ -91,7 +91,8 @@ def get_capacity_size(value): return capacity @staticmethod - def split_value_map_list(value_info, map_list, is_alert=False, split=":"): + def split_value_map_list(value_info, map_list, is_mapping=False, + is_alert=False, split=":"): detail_array = value_info.split('\r\n') value_map = {} temp_key = '' @@ -100,7 +101,7 @@ def split_value_map_list(value_info, map_list, is_alert=False, split=":"): string_info = detail.split(split + " ") key = string_info[0].replace(' ', '') value = '' - if len(string_info) > 1: + if len(string_info) > 1 or is_mapping: for string in string_info[1:]: value = string.replace('""', '') value_map[key] = value diff --git a/delfin/tests/unit/drivers/netapp/netapp_ontap/test_constans.py b/delfin/tests/unit/drivers/netapp/netapp_ontap/test_constans.py index 6d7c8c1b8..a943d5737 100644 --- a/delfin/tests/unit/drivers/netapp/netapp_ontap/test_constans.py +++ b/delfin/tests/unit/drivers/netapp/netapp_ontap/test_constans.py @@ -1805,3 +1805,261 @@ "duration": "PT15S" } ] + +FC_INITIATOR_INFO = """ Vserver: PSA-xiejun00295347\r + Logical Interface: PSA02-xiejun\r + Initiator WWPN: 10:00:00:00:c9:d5:b9:6e\r + Port Address: 50400\r + Initiator WWNN: 20:00:00:00:c9:d5:b9:6e\r +Initiator WWPN Alias: -\r + Igroup Name: test00101, Test_MKL_Suse_8.44.133.65\r +\r + Vserver: SVC_FC\r + Logical Interface: Migration_NetApp02_0c_02\r + Initiator WWPN: 10:00:00:00:c9:d5:b9:6e\r + Port Address: 50400\r + Initiator WWNN: 20:00:00:00:c9:d5:b9:6e\r +Initiator WWPN Alias: -\r + Igroup Name: -\r +\r + Vserver: SVM_VDF\r + Logical Interface: VDF_test02\r + Initiator WWPN: 10:00:00:00:c9:d5:b9:6e\r + Port Address: 50400\r + Initiator WWNN: 20:00:00:00:c9:d5:b9:6e\r +Initiator WWPN Alias: -\r + Igroup Name: -\r +\r + Vserver: xiejun_00295347\r + Logical Interface: xiejun_port1\r + Initiator WWPN: 10:00:00:00:c9:d5:b9:6e\r + Port Address: 50400\r + Initiator WWNN: 20:00:00:00:c9:d5:b9:6e\r +Initiator WWPN Alias: -\r + Igroup Name: -\r +4 entries were displayed. +""" + +ISCSI_INITIATOR_INFO = """ Vserver: svm3\r + Target Portal Group: zb_IPV602\r + Target Session ID: 357\r +Initiator Name: iqn.2006-08.com.huawei:21004447dcca426::01\r + Initiator Alias : -\r + TPGroup Tag: 1062 \r + Initiator Session ID: 80:12:34:58:78:9a\r + Igroup Name: -""" + +HOSTS_INFO = """ Vserver Name: svm1\r + Igroup Name: fcstart1\r + Protocol: mixed\r + OS Type: linux\r +Portset Binding Igroup: portgroup\r + Igroup UUID: c5ca5750-121f-11ec-b66c-000c29bfc4d7\r + ALUA: true\r + Initiators: 20:01:00:0c:29:bf:c4:d7 (not logged in)\r +10:00:00:00:c9:d5:b9:6e (not logged in)\r +iqn.2006-08.com.huawei:21004447dcca426::0 (not logged in)\r +\r + Vserver Name: svm3\r + Igroup Name: svm3\r + Protocol: mixed\r + OS Type: windows\r +Portset Binding Igroup: portgroup2\r + Igroup UUID: 9a6c2496-174b-11ec-b66c-000c29bfc4d7\r + ALUA: true\r +Initiators: iqn.2006-08.com.huawei:21004447dcca426::0 (not logged in)\r +10:00:00:00:c9:d5:b9:6e (not logged in)""" + +PORT_SET_INFO = """ Vserver Name: svm1\r + Portset Name: portgroup\r + LIF Or TPG Name: ontap-01_fc_lif_1, ontap-01_fcoe_lif_1, fc1\r + Protocol: fcp\r + Number Of Ports: 3\r +Bound To Igroups: fcstart1\r +\r + Vserver Name: svm3\r + Portset Name: portgroup2\r + LIF Or TPG Name: ontap-01_iscsi_lif_1\r + Protocol: iscsi\r + Number Of Ports: 1\r +Bound To Igroups: svm3\r +2 entries were displayed.""" + +LIF_INFO = """ Vserver Name: svm1\r + Logical Interface Name: ontap-01_fc_lif_1\r + Role: data\r + Data Protocol: fcp\r + Home Node: ontap-01\r + Home Port: 0a\r + Current Node: ontap-01\r + Current Port: 0a\r + Operational Status: down\r + Extended Status: Groovy, man!\r + Is Home: true\r + Network Address: -\r + Netmask: -\r + Bits in the Netmask: -\r + Subnet Name: -\r + Administrative Status: up\r + Failover Policy: disabled\r + Firewall Policy: -\r + Auto Revert: false\r + Fully Qualified DNS Zone Name: none\r + DNS Query Listen Enable: -\r + Failover Group Name: -\r + FCP WWPN: 20:00:00:0c:29:bf:c4:d7\r + Address family: -\r + Comment: -\r + IPspace of LIF: -\r + Is Dynamic DNS Update Enabled?: -\r +\r + Vserver Name: svm1\r + Logical Interface Name: ontap-01_fcoe_lif_1\r + Role: data\r + Data Protocol: fcp\r + Home Node: ontap-01\r + Home Port: 0c\r + Current Node: ontap-01\r + Current Port: 0c\r + Operational Status: down\r + Extended Status: Groovy, man!\r + Is Home: true\r + Network Address: -\r + Netmask: -\r + Bits in the Netmask: -\r + Subnet Name: -\r + Administrative Status: up\r + Failover Policy: disabled\r + Firewall Policy: -\r + Auto Revert: false\r + Fully Qualified DNS Zone Name: none\r + DNS Query Listen Enable: -\r + Failover Group Name: -\r + FCP WWPN: 20:01:00:0c:29:bf:c4:d7\r + Address family: -\r + Comment: -\r + IPspace of LIF: -\r + Is Dynamic DNS Update Enabled?: -\r +\r + Vserver Name: svm3\r + Logical Interface Name: ontap-01_iscsi_lif_1\r + Role: data\r + Data Protocol: iscsi\r + Home Node: ontap-01\r + Home Port: e0a\r + Current Node: ontap-01\r + Current Port: e0a\r + Operational Status: up\r + Extended Status: -\r + Is Home: true\r + Network Address: 192.168.159.140\r + Netmask: 255.255.255.0\r + Bits in the Netmask: 24\r + Subnet Name: -\r + Administrative Status: up\r + Failover Policy: disabled\r + Firewall Policy: data\r + Auto Revert: false\r + Fully Qualified DNS Zone Name: none\r + DNS Query Listen Enable: false\r + Failover Group Name: -\r + FCP WWPN: -\r + Address family: ipv4\r + Comment: -\r + IPspace of LIF: Default\r + Is Dynamic DNS Update Enabled?: false""" + +LUN_MAPPING_INFO = """ Vserver Name: svm1\r + LUN Path: /vol/lun_1_vol/lun_1\r + Volume Name: lun_1_vol\r + Qtree Name: ""\r + LUN Name: lun_1\r + Igroup Name: fcstart1\r + Igroup OS Type: windows\r + Igroup Protocol Type: fcp\r + LUN ID: 123\r +Portset Binding Igroup: portgroup\r + ALUA: true\r + Initiators: 20:00:00:0c:29:bf:c4:d7, 10:00:00:00:c9:d5:b9:6e\r + LUN Node: ontap-01\r + Reporting Nodes: ontap-01\r +\r + Vserver Name: svm3\r + LUN Path: /vol/svm3_lun/svm3_lun\r + Volume Name: svm3_lun\r + Qtree Name: ""\r + LUN Name: svm3_lun\r + Igroup Name: svm3\r + Igroup OS Type: windows\r + Igroup Protocol Type: iscsi\r + LUN ID: 0\r +Portset Binding Igroup: portgroup2\r + ALUA: true\r + Initiators: iqn.2006-08.com.huawei:21004447dcca426::0\r + LUN Node: ontap-01\r + Reporting Nodes: ontap-01\r +2 entries were displayed.""" + +MAPPING_LUN_INFO = """ Vserver Name: svm1\r + LUN Path: /vol/lun_1_vol/lun_1\r + Volume Name: lun_1_vol\r + Qtree Name: ""\r + LUN Name: lun_1\r + LUN Size: 1.00GB\r + OS Type: windows_2008\r + Space Reservation: enabled\r + Serial Number: wpEzy]RQjLqN\r + Serial Number (Hex): 7770457a795d52516a4c714e\r + Comment:\r +Space Reservations Honored: true\r + Space Allocation: disabled\r + State: online\r + LUN UUID: 2aa5a7ab-efbe-41f3-a4bf-dcd741e641a1\r + Mapped: mapped\r + Device Legacy ID: -\r + Device Binary ID: -\r + Device Text ID: -\r + Read Only: false\r + Fenced Due to Restore: false\r + Used Size: 0\r + Maximum Resize Size: 502.0GB\r + Creation Time: 9/10/2021 09:57:47\r + Class: regular\r + Node Hosting the LUN: ontap-01\r + QoS Policy Group: -\r + Caching Policy Name: -\r + Clone: false\r + Clone Autodelete Enabled: false\r + Inconsistent Import: false\r +\r + Vserver Name: svm3\r + LUN Path: /vol/svm3_lun/svm3_lun\r + Volume Name: svm3_lun\r + Qtree Name: ""\r + LUN Name: svm3_lun\r + LUN Size: 1.00GB\r + OS Type: windows_2008\r + Space Reservation: enabled\r + Serial Number: wpEzy]RQjLqA\r + Serial Number (Hex): 7770457a795d52516a4c714e\r + Comment:\r +Space Reservations Honored: true\r + Space Allocation: disabled\r + State: online\r + LUN UUID: 2aa5a7ab-efbe-41f3-a4bf-dcd741e624a1\r + Mapped: mapped\r + Device Legacy ID: -\r + Device Binary ID: -\r + Device Text ID: -\r + Read Only: false\r + Fenced Due to Restore: false\r + Used Size: 0\r + Maximum Resize Size: 502.0GB\r + Creation Time: 9/10/2021 09:57:47\r + Class: regular\r + Node Hosting the LUN: ontap-01\r + QoS Policy Group: -\r + Caching Policy Name: -\r + Clone: false\r + Clone Autodelete Enabled: false\r + Inconsistent Import: false""" diff --git a/delfin/tests/unit/drivers/netapp/netapp_ontap/test_netapp.py b/delfin/tests/unit/drivers/netapp/netapp_ontap/test_netapp.py index 7ff9df641..db6bf7c68 100644 --- a/delfin/tests/unit/drivers/netapp/netapp_ontap/test_netapp.py +++ b/delfin/tests/unit/drivers/netapp/netapp_ontap/test_netapp.py @@ -203,6 +203,35 @@ def test_get_capabilities(self): self.assertEqual(data['resource_metrics']['storage'] ['throughput']['unit'], 'MB/s') + def test_list_storage_host_initiators(self): + SSHPool.do_exec = mock.Mock( + side_effect=[test_constans.ISCSI_INITIATOR_INFO, + test_constans.FC_INITIATOR_INFO, + test_constans.HOSTS_INFO]) + data = self.netapp_client.list_storage_host_initiators(context) + self.assertEqual(data[0]['name'], '20:01:00:0c:29:bf:c4:d7') + + def test_list_port_groups(self): + SSHPool.do_exec = mock.Mock( + side_effect=[test_constans.PORT_SET_INFO, + test_constans.LIF_INFO]) + data = self.netapp_client.list_port_groups(context) + self.assertEqual(data['port_groups'][0]['name'], 'portgroup') + + def test_list_storage_hosts(self): + SSHPool.do_exec = mock.Mock( + side_effect=[test_constans.HOSTS_INFO]) + data = self.netapp_client.list_storage_hosts(context) + self.assertEqual(data[0]['name'], 'fcstart1') + + def test_list_masking_views(self): + SSHPool.do_exec = mock.Mock( + side_effect=[test_constans.LUN_MAPPING_INFO, + test_constans.MAPPING_LUN_INFO, + test_constans.HOSTS_INFO]) + data = self.netapp_client.list_masking_views(context) + self.assertEqual(data[0]['name'], 'fcstart1_lun_1') + def test_get_latest_perf_timestamp(self): self.netapp_client.netapp_handler.do_rest_call = mock.Mock( side_effect=[test_constans.CLUSTER_PER_INFO]) From effad04e0ae3e0e45d39436967f1a21169476224 Mon Sep 17 00:00:00 2001 From: zhilong-xu <81213962+zhilong-xu@users.noreply.github.com> Date: Wed, 18 May 2022 16:35:09 +0800 Subject: [PATCH 17/17] add Pure mapping (#820) --- delfin/drivers/pure/flasharray/consts.py | 19 + .../pure/flasharray/pure_flasharray.py | 153 +- .../drivers/pure/flasharray/rest_handler.py | 6 + .../pure/flasharray/test_pure_flasharray.py | 1383 ++++++++++++++++- 4 files changed, 1546 insertions(+), 15 deletions(-) diff --git a/delfin/drivers/pure/flasharray/consts.py b/delfin/drivers/pure/flasharray/consts.py index 7226a1c9f..c2b2ab9d4 100644 --- a/delfin/drivers/pure/flasharray/consts.py +++ b/delfin/drivers/pure/flasharray/consts.py @@ -94,3 +94,22 @@ PARSE_ALERT_SEVERITY_MAP = {'1': constants.Severity.WARNING, '2': constants.Severity.INFORMATIONAL} + +HOST_OS_TYPES_MAP = { + 'linux': constants.HostOSTypes.LINUX, + 'windows': constants.HostOSTypes.WINDOWS, + 'solaris': constants.HostOSTypes.SOLARIS, + 'hp-ux': constants.HostOSTypes.HP_UX, + 'hpux': constants.HostOSTypes.HP_UX, + 'aix': constants.HostOSTypes.AIX, + 'xenserver': constants.HostOSTypes.XEN_SERVER, + 'vmware esx': constants.HostOSTypes.VMWARE_ESX, + 'esxi': constants.HostOSTypes.VMWARE_ESX, + 'linux_vis': constants.HostOSTypes.LINUX_VIS, + 'windows server 2012': constants.HostOSTypes.WINDOWS_SERVER_2012, + 'oracle vm': constants.HostOSTypes.ORACLE_VM, + 'oracle-vm-server': constants.HostOSTypes.ORACLE_VM, + 'open vms': constants.HostOSTypes.OPEN_VMS, + 'vms': constants.HostOSTypes.OPEN_VMS, + 'unknown': constants.HostOSTypes.UNKNOWN +} diff --git a/delfin/drivers/pure/flasharray/pure_flasharray.py b/delfin/drivers/pure/flasharray/pure_flasharray.py index 92feb48cf..f5a9bfb5f 100644 --- a/delfin/drivers/pure/flasharray/pure_flasharray.py +++ b/delfin/drivers/pure/flasharray/pure_flasharray.py @@ -129,7 +129,7 @@ def list_alerts(self, context, query_para=None): timestamp = (int(datetime.datetime.strptime (opened, '%Y-%m-%dT%H:%M:%SZ').timestamp() + time_difference) * - consts.DEFAULT_LIST_ALERTS_TIME_CONVERSION) if\ + consts.DEFAULT_LIST_ALERTS_TIME_CONVERSION) if \ opened is not None else None if query_para is not None: try: @@ -362,3 +362,154 @@ def reset_connection(self, context, **kwargs): @staticmethod def get_access_url(): return 'https://{ip}' + + def list_storage_host_initiators(self, context): + list_initiators = [] + initiators = self.rest_handler.rest_call( + self.rest_handler.REST_HOST_URL) + for initiator in (initiators or []): + host_id = initiator.get('name') + self.get_initiator(initiator, list_initiators, host_id, 'iqn', + constants.InitiatorType.ISCSI) + self.get_initiator(initiator, list_initiators, host_id, 'wwn', + constants.InitiatorType.FC) + self.get_initiator(initiator, list_initiators, host_id, 'nqn', + constants.InitiatorType.NVME_OVER_FABRIC) + return list_initiators + + def get_initiator(self, initiator, list_initiators, host_id, protocol, + network): + protocol_list = initiator.get(protocol) + if protocol_list: + for initiator_protocol in (protocol_list or []): + if 'wwn' in protocol: + initiator_protocol = self.get_splice_wwn( + initiator_protocol) + initiator_d = { + 'native_storage_host_initiator_id': initiator_protocol, + 'native_storage_host_id': host_id, + 'name': initiator_protocol, + 'type': network, + 'status': constants.InitiatorStatus.UNKNOWN, + 'wwn': initiator_protocol, + 'storage_id': self.storage_id + } + list_initiators.append(initiator_d) + + def list_storage_hosts(self, ctx): + host_list = [] + hosts = self.rest_handler.rest_call( + self.rest_handler.REST_HOST_PERSONALITY_URL) + for host in (hosts or []): + name = host.get('name') + personality = host.get('personality').lower()\ + if host.get('personality') else None + h = { + "name": name, + "storage_id": self.storage_id, + "native_storage_host_id": name, + "os_type": consts.HOST_OS_TYPES_MAP.get( + personality, constants.HostOSTypes.UNKNOWN), + "status": constants.HostStatus.NORMAL + } + host_list.append(h) + return host_list + + def list_storage_host_groups(self, context): + host_groups = self.rest_handler.rest_call( + self.rest_handler.REST_HGROUP_URL) + host_group_list = [] + storage_host_grp_relation_list = [] + for hgroup in (host_groups or []): + name = hgroup.get('name') + hg = { + 'native_storage_host_group_id': name, + 'name': name, + 'storage_id': self.storage_id + } + host_group_list.append(hg) + for host in (hgroup.get('hosts') or []): + host_relation = { + 'native_storage_host_group_id': name, + 'storage_id': self.storage_id, + 'native_storage_host_id': host + } + storage_host_grp_relation_list.append(host_relation) + result = { + 'storage_host_groups': host_group_list, + 'storage_host_grp_host_rels': storage_host_grp_relation_list + } + return result + + def list_volume_groups(self, context): + volume_groups = self.rest_handler.rest_call( + self.rest_handler.REST_VOLUME_GROUP_URL) + vol_group_list = [] + vol_grp_vol_relation_list = [] + for volume_group in (volume_groups or []): + name = volume_group.get('name') + vol_g = { + 'name': name, + 'storage_id': self.storage_id, + 'native_volume_group_id': name + } + vol_group_list.append(vol_g) + for volume_id in (volume_group.get('volumes') or []): + volume_group_relation = { + 'storage_id': self.storage_id, + 'native_volume_group_id': name, + 'native_volume_id': volume_id + } + vol_grp_vol_relation_list.append(volume_group_relation) + result = { + 'volume_groups': vol_group_list, + 'vol_grp_vol_rels': vol_grp_vol_relation_list + } + return result + + def list_masking_views(self, context): + list_masking_views = [] + view_id_dict = {} + hgroup_views = self.rest_handler.rest_call( + self.rest_handler.REST_HGROUP_CONNECT_URL) + for hgroup_view in (hgroup_views or []): + hgroup_name = hgroup_view.get('name') + native_volume_id = hgroup_view.get('vol') + native_masking_view_id = '{}{}'.format( + hgroup_name, native_volume_id) + if view_id_dict.get(hgroup_name): + continue + view_id_dict[native_masking_view_id] = hgroup_name + view = { + 'native_masking_view_id': native_masking_view_id, + 'name': native_masking_view_id, + 'native_storage_host_group_id': hgroup_name, + 'native_volume_id': native_volume_id, + 'storage_id': self.storage_id + } + list_masking_views.append(view) + + masking_views = self.rest_handler.rest_call( + self.rest_handler.REST_HOST_CONNECT_URL) + for masking_view in (masking_views or []): + hgroup = masking_view.get('hgroup') + host_id = masking_view.get('name') + native_volume_id = masking_view.get('vol') + hgroup_name = '{}{}'.format(hgroup, native_volume_id) + if view_id_dict.get(hgroup_name) is not None and\ + view_id_dict.get(hgroup_name) in hgroup: + continue + native_masking_view_id = '{}{}{}'.format( + host_id, hgroup, native_volume_id) + if view_id_dict.get(native_masking_view_id): + continue + view_id_dict[native_masking_view_id] = native_masking_view_id + view = { + 'native_masking_view_id': native_masking_view_id, + 'name': native_masking_view_id, + 'native_storage_host_id': host_id, + 'native_volume_id': native_volume_id, + 'storage_id': self.storage_id + } + list_masking_views.append(view) + return list_masking_views diff --git a/delfin/drivers/pure/flasharray/rest_handler.py b/delfin/drivers/pure/flasharray/rest_handler.py index 39570041a..b46cb0740 100644 --- a/delfin/drivers/pure/flasharray/rest_handler.py +++ b/delfin/drivers/pure/flasharray/rest_handler.py @@ -35,6 +35,12 @@ class RestHandler(RestClient): REST_ALERTS_URL = '/api/1.17/message?flagged=true&open=true' REST_AUTH_URL = '/api/1.17/auth/apitoken' REST_SESSION_URL = '/api/1.17/auth/session' + REST_HOST_URL = '/api/1.17/host' + REST_HOST_PERSONALITY_URL = '/api/1.17/host?personality=true' + REST_HOST_CONNECT_URL = '/api/1.17/host?connect=true' + REST_HGROUP_CONNECT_URL = '/api/1.17/hgroup?connect=true' + REST_HGROUP_URL = '/api/1.17/hgroup' + REST_VOLUME_GROUP_URL = '/api/1.17/vgroup' def __init__(self, **kwargs): super(RestHandler, self).__init__(**kwargs) diff --git a/delfin/tests/unit/drivers/pure/flasharray/test_pure_flasharray.py b/delfin/tests/unit/drivers/pure/flasharray/test_pure_flasharray.py index 6c1046088..1dd9d1374 100644 --- a/delfin/tests/unit/drivers/pure/flasharray/test_pure_flasharray.py +++ b/delfin/tests/unit/drivers/pure/flasharray/test_pure_flasharray.py @@ -21,9 +21,11 @@ from delfin import context from delfin.drivers.pure.flasharray.rest_handler import RestHandler from delfin.drivers.pure.flasharray.pure_flasharray import PureFlashArrayDriver + LOG = log.getLogger(__name__) ACCESS_INFO = { + "storage_id": "12345", "rest": { "host": "10.0.0.1", "port": 8443, @@ -288,6 +290,1335 @@ "username": "username", "status": 200 } +hosts_info = [ + { + "iqn": [ + "iqn.1996-04.de.suse:01:ca9f3bcaf47" + ], + "wwn": [], + "nqn": [], + "name": "host", + "hgroup": "HGTest" + }, + { + "iqn": [], + "wwn": [], + "nqn": [], + "name": "wxth", + "hgroup": None + }, + { + "iqn": [ + "iqn.1991-05.com.microsoft:win3" + ], + "wwn": [], + "nqn": [], + "name": "huhuitest", + "hgroup": None + }, + { + "iqn": [], + "wwn": [], + "nqn": [], + "name": "testGroup", + "hgroup": None + }, + { + "iqn": [], + "wwn": [ + "21000024FF2C9524", + "21000024FF2C9525" + ], + "nqn": [], + "name": "windows223", + "hgroup": None + }, + { + "iqn": [], + "wwn": [ + "10000000C9D5BC06", + "10000000C9D5BC07" + ], + "nqn": [], + "name": "CL-B06-RH2288HV3-8-44-157-33", + "hgroup": None + }, + { + "iqn": [], + "wwn": [ + "21000024FF76D0CC", + "21000024FF76D0CD" + ], + "nqn": [], + "name": "CL-C21-RH5885HV3-8-44-165-22", + "hgroup": None + }, + { + "iqn": [ + "iqn.1996-04.de.suse:01:66bf70288332" + ], + "wwn": [], + "nqn": [], + "name": "test-1s", + "hgroup": None + }, + { + "iqn": [], + "wwn": [], + "nqn": [], + "name": "rhev125", + "hgroup": None + }, + { + "iqn": [], + "wwn": [ + "210034800D6E7ADE", + "210034800D6E7ADF" + ], + "nqn": [], + "name": "QIB", + "hgroup": "QIB" + }, + { + "iqn": [], + "wwn": [ + "20090002D2937E9F", + "20190002D2937E9F" + ], + "nqn": [], + "name": "v6-8-44-128-21", + "hgroup": None + }, + { + "iqn": [ + "iqn.1994-05.com.redhat:1a9eaa70b558" + ], + "wwn": [], + "nqn": [], + "name": "host135", + "hgroup": None + }, + { + "iqn": [], + "wwn": [ + "2200CC05777C3EDF", + "2210CC05777C3EDF" + ], + "nqn": [], + "name": "zty-doradoV6", + "hgroup": None + }, + { + "iqn": [ + "iqn.1994-05.com.redhat:71cfb5b97df" + ], + "wwn": [ + "21000024FF76D0CF" + ], + "nqn": [], + "name": "CL-Test1", + "hgroup": None + }, + { + "iqn": [ + "iqn.1994-05.com.redhat:80c412848b94" + ], + "wwn": [], + "nqn": [], + "name": "host137", + "hgroup": None + }, + { + "iqn": [], + "wwn": [], + "nqn": [], + "name": "hsesxi", + "hgroup": None + }, + { + "iqn": [], + "wwn": [ + "21000024FF40272A", + "21000024FF40272B" + ], + "nqn": [], + "name": "zty-windows", + "hgroup": None + }, + { + "iqn": [], + "wwn": [], + "nqn": [], + "name": "hosttest", + "hgroup": "HGTest" + }, + { + "iqn": [], + "wwn": [ + "21000024FF5351F0", + "21000024FF5351F1" + ], + "nqn": [], + "name": "hswin41", + "hgroup": None + }, + { + "iqn": [], + "wwn": [], + "nqn": [], + "name": "ztj201", + "hgroup": None + }, + { + "iqn": [], + "wwn": [], + "nqn": [], + "name": "test123", + "hgroup": None + }, + { + "iqn": [], + "wwn": [], + "nqn": [], + "name": "zsytest", + "hgroup": None + }, + { + "iqn": [], + "wwn": [], + "nqn": [ + "nqn.2021-12.org.nvmexpress.mytest" + ], + "name": "zhilong-host0000002130", + "hgroup": None + } +] +HOSTS_PERSONALITY_INFO = [ + { + "name": "host", + "personality": None + }, + { + "name": "wxth", + "personality": None + }, + { + "name": "huhuitest", + "personality": None + }, + { + "name": "testGroup", + "personality": None + }, + { + "name": "windows223", + "personality": None + }, + { + "name": "CL-B06-RH2288HV3-8-44-157-33", + "personality": None + }, + { + "name": "CL-C21-RH5885HV3-8-44-165-22", + "personality": None + }, + { + "name": "test-1s", + "personality": None + }, + { + "name": "rhev125", + "personality": None + }, + { + "name": "QIB", + "personality": None + }, + { + "name": "v6-8-44-128-21", + "personality": None + }, + { + "name": "host135", + "personality": None + }, + { + "name": "zty-doradoV6", + "personality": None + }, + { + "name": "CL-Test1", + "personality": None + }, + { + "name": "host137", + "personality": None + }, + { + "name": "hsesxi", + "personality": None + }, + { + "name": "zty-windows", + "personality": None + }, + { + "name": "hosttest", + "personality": None + }, + { + "name": "hswin41", + "personality": None + }, + { + "name": "ztj201", + "personality": None + }, + { + "name": "test123", + "personality": None + }, + { + "name": "zsytest", + "personality": None + }, + { + "name": "zhilong-host0000002130", + "personality": "aix" + } +] +HGROUP_INFO = [ + { + "hosts": [], + "name": "podgroup" + }, + { + "hosts": [], + "name": "NewTest" + }, + { + "hosts": [ + "QIB" + ], + "name": "QIB" + }, + { + "hosts": [ + "host", + "hosttest" + ], + "name": "HGTest" + } +] + +VOLUME_GROUP_INFO = [ + { + "name": "vvol-pure-VM1-072e131e-vg", + "volumes": [] + }, + { + "name": "vvol-pure-vm2-e48a0ef8-vg", + "volumes": [] + }, + { + "name": "vvol-pure-vm3-65d42a4e-vg", + "volumes": [] + }, + { + "name": "vvol-pure-vm4-17c41971-vg", + "volumes": [] + }, + { + "name": "Volume-Group", + "volumes": [ + "Volume-Group/voltest001", + "Volume-Group/voltest002", + "Volume-Group/voltest003", + "Volume-Group/voltest004", + "Volume-Group/voltest005" + ] + }, + { + "name": "test1", + "volumes": [] + }, + { + "name": "tangxuan", + "volumes": [] + } +] +HOSTS_CONNECT_INFO = [ + { + "vol": "huhuitest", + "name": "huhuitest", + "lun": 1, + "hgroup": None + }, + { + "vol": "test", + "name": "wxth", + "lun": 1, + "hgroup": None + }, + { + "vol": "test", + "name": "testGroup", + "lun": 1, + "hgroup": None + }, + { + "vol": "win2016_223", + "name": "windows223", + "lun": 1, + "hgroup": None + }, + { + "vol": "pure-protocol-endpoint", + "name": "CL-C21-RH5885HV3-8-44-165-22", + "lun": 1, + "hgroup": None + }, + { + "vol": "CL_VOLUME_1_remote", + "name": "CL-C21-RH5885HV3-8-44-165-22", + "lun": 2, + "hgroup": None + }, + { + "vol": "lun-test1s", + "name": "test-1s", + "lun": 1, + "hgroup": None + }, + { + "vol": "QIB1", + "name": "QIB", + "lun": 254, + "hgroup": "QIB" + }, + { + "vol": "QIB1", + "name": "zty-windows", + "lun": 254, + "hgroup": "QIB" + }, + { + "vol": "QIB2", + "name": "zty-windows", + "lun": 253, + "hgroup": "QIB" + }, + { + "vol": "QIB2", + "name": "QIB", + "lun": 253, + "hgroup": "QIB" + }, + { + "vol": "yzw_iotest", + "name": "host135", + "lun": 2, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000003", + "name": "host137", + "lun": 3, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000009", + "name": "host135", + "lun": 3, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000012", + "name": "host135", + "lun": 6, + "hgroup": None + }, + { + "vol": "v6-8-44-128-21", + "name": "v6-8-44-128-21", + "lun": 1, + "hgroup": None + }, + { + "vol": "V6-8-44-128-21-002", + "name": "v6-8-44-128-21", + "lun": 2, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000007", + "name": "host137", + "lun": 7, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000010", + "name": "host135", + "lun": 4, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000013", + "name": "host137", + "lun": 2, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000000", + "name": "host135", + "lun": 5, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000001", + "name": "host137", + "lun": 4, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000016", + "name": "host137", + "lun": 5, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000018", + "name": "host135", + "lun": 7, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000015", + "name": "host135", + "lun": 8, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000020", + "name": "host137", + "lun": 6, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000021", + "name": "host135", + "lun": 9, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000022", + "name": "host137", + "lun": 8, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000019", + "name": "host135", + "lun": 10, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000026", + "name": "host137", + "lun": 9, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000028", + "name": "host135", + "lun": 11, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000024", + "name": "host137", + "lun": 10, + "hgroup": None + }, + { + "vol": "hsboot", + "name": "hsesxi", + "lun": 1, + "hgroup": None + }, + { + "vol": "hszdata", + "name": "hsesxi", + "lun": 2, + "hgroup": None + }, + { + "vol": "zty_lun16", + "name": "zty-doradoV6", + "lun": 1, + "hgroup": None + }, + { + "vol": "zty_lun15", + "name": "zty-doradoV6", + "lun": 2, + "hgroup": None + }, + { + "vol": "zty_lun13", + "name": "zty-doradoV6", + "lun": 3, + "hgroup": None + }, + { + "vol": "zty_lun11", + "name": "zty-doradoV6", + "lun": 4, + "hgroup": None + }, + { + "vol": "zty_lun14", + "name": "zty-doradoV6", + "lun": 5, + "hgroup": None + }, + { + "vol": "zty_lun2", + "name": "zty-doradoV6", + "lun": 6, + "hgroup": None + }, + { + "vol": "zty_lun5", + "name": "zty-doradoV6", + "lun": 7, + "hgroup": None + }, + { + "vol": "zty_lun4", + "name": "zty-doradoV6", + "lun": 8, + "hgroup": None + }, + { + "vol": "zty_lun1", + "name": "zty-doradoV6", + "lun": 9, + "hgroup": None + }, + { + "vol": "zty_lun3", + "name": "zty-doradoV6", + "lun": 10, + "hgroup": None + }, + { + "vol": "zty_lun6", + "name": "zty-doradoV6", + "lun": 11, + "hgroup": None + }, + { + "vol": "zty_lun12", + "name": "zty-doradoV6", + "lun": 12, + "hgroup": None + }, + { + "vol": "zty_lun10", + "name": "zty-doradoV6", + "lun": 13, + "hgroup": None + }, + { + "vol": "zty_lun8", + "name": "zty-doradoV6", + "lun": 14, + "hgroup": None + }, + { + "vol": "zty_lun7", + "name": "zty-doradoV6", + "lun": 15, + "hgroup": None + }, + { + "vol": "zty_lun9", + "name": "zty-doradoV6", + "lun": 16, + "hgroup": None + }, + { + "vol": "Volume-Group/voltest005", + "name": "hosttest", + "lun": 254, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest005", + "name": "host", + "lun": 254, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest001", + "name": "host", + "lun": 253, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest001", + "name": "hosttest", + "lun": 253, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest002", + "name": "host", + "lun": 252, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest002", + "name": "hosttest", + "lun": 252, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest003", + "name": "host", + "lun": 251, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest003", + "name": "hosttest", + "lun": 251, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest004", + "name": "hosttest", + "lun": 250, + "hgroup": "HGTest" + }, + { + "vol": "Volume-Group/voltest004", + "name": "host", + "lun": 250, + "hgroup": "HGTest" + }, + { + "vol": "homelab-pso-db_0000000001-u", + "name": "CL-B06-RH2288HV3-8-44-157-33", + "lun": 4, + "hgroup": None + }, + { + "vol": "Volume-Group/voltest001", + "name": "CL-B06-RH2288HV3-8-44-157-33", + "lun": 1, + "hgroup": None + }, + { + "vol": "hswin4102", + "name": "zhilong-host0000002130", + "lun": 1, + "hgroup": None + }, + { + "vol": "tangxuan/tt001", + "name": "host135", + "lun": 1, + "hgroup": None + }, + { + "vol": "hswin", + "name": "CL-Test1", + "lun": 1, + "hgroup": None + }, + { + "vol": "homelab-pso-db_0000000000-u", + "name": "zhilong-host0000002130", + "lun": 2, + "hgroup": None + }, + { + "vol": "nc::136_connect", + "name": "hosttest", + "lun": 1, + "hgroup": None + } +] +HGROUP_CONNECT_INFO = [ + { + "vol": "QIB1", + "name": "QIB", + "lun": 254 + }, + { + "vol": "QIB2", + "name": "QIB", + "lun": 253 + }, + { + "vol": "Volume-Group/voltest005", + "name": "HGTest", + "lun": 254 + }, + { + "vol": "Volume-Group/voltest001", + "name": "HGTest", + "lun": 253 + }, + { + "vol": "Volume-Group/voltest002", + "name": "HGTest", + "lun": 252 + }, + { + "vol": "Volume-Group/voltest003", + "name": "HGTest", + "lun": 251 + }, + { + "vol": "Volume-Group/voltest004", + "name": "HGTest", + "lun": 250 + }, + { + "vol": "homelab-pso-db_0000000002", + "name": "NewTest", + "lun": 254 + }, + { + "vol": "yzw_test0", + "name": "zhilong-hg", + "lun": 254 + } +] +volume_data = [ + {'native_volume_id': 'oracl_ail', 'name': 'oracl_ail', + 'total_capacity': 2156324555567, 'used_capacity': 116272464547, + 'free_capacity': 2040052091020, 'storage_id': '12345', 'status': 'normal', + 'type': 'thin'}, + {'native_volume_id': 'wxt1', 'name': 'wxt1', 'total_capacity': 1073741824, + 'used_capacity': 0, 'free_capacity': 1073741824, 'storage_id': '12345', + 'status': 'normal', 'type': 'thin'}] +storage_data = { + 'model': 'FA-m20r2', 'total_capacity': 122276719419392, + 'raw_capacity': 3083686627641, 'used_capacity': 324829845504, + 'free_capacity': 121951889573888, 'vendor': 'PURE', 'name': 'pure01', + 'serial_number': 'dlmkk15xcfdf4v5', 'firmware_version': '4.6.7', + 'status': 'normal'} +list_alert_data = [ + {'occur_time': 1526122521000, 'alert_id': 135, 'severity': 'Warning', + 'category': 'Fault', 'location': 'ct1.eth0', 'type': 'EquipmentAlarm', + 'resource_type': 'Storage', 'alert_name': 'failure', + 'match_key': '7f1de29e6da19d22b51c68001e7e0e54', + 'description': '(hardware:ct1.eth0): failure'}, + {'occur_time': 1526122521000, 'alert_id': 10088786, 'severity': 'Warning', + 'category': 'Fault', 'location': 'ct1.ntpd', 'type': 'EquipmentAlarm', + 'resource_type': 'Storage', 'alert_name': 'server unreachable', + 'match_key': 'b35a0c63d4cd82256b95f51522c6ba32', + 'description': '(process:ct1.ntpd): server unreachable'}] +parse_alert_data = { + 'alert_id': '30007589', 'severity': 'Informational', 'category': 'Fault', + 'occur_time': 1644833673861, 'description': '(None:cto): server error', + 'location': 'cto', 'type': 'EquipmentAlarm', 'resource_type': 'Storage', + 'alert_name': 'cto.server error', 'sequence_number': '30007589', + 'match_key': '11214c87bb6efcf8dc2aed1095271774'} +controllers_data = [ + {'name': 'CT0', 'status': 'unknown', 'soft_version': '5.3.0', + 'storage_id': '12345', 'native_controller_id': 'CT0', 'location': 'CT0'}, + {'name': 'CT1', 'status': 'unknown', 'soft_version': '5.3.0', + 'storage_id': '12345', 'native_controller_id': 'CT1', 'location': 'CT1'}] +disk_data = [ + {'name': 'CH0.BAY1', 'physical_type': 'ssd', 'status': 'normal', + 'storage_id': '12345', 'capacity': 1027895542547, 'speed': None, + 'model': None, 'serial_number': None, 'native_disk_id': 'CH0.BAY1', + 'location': 'CH0.BAY1', 'manufacturer': 'PURE', 'firmware': ''}, + {'name': 'CH0.BAY2', 'physical_type': 'ssd', 'status': 'normal', + 'storage_id': '12345', 'capacity': 1027895542547, 'speed': None, + 'model': None, 'serial_number': None, 'native_disk_id': 'CH0.BAY2', + 'location': 'CH0.BAY2', 'manufacturer': 'PURE', 'firmware': ''}, + {'name': 'CH0.BAY3', 'physical_type': 'ssd', 'status': 'normal', + 'storage_id': '12345', 'capacity': 1027895542547, 'speed': None, + 'model': None, 'serial_number': None, 'native_disk_id': 'CH0.BAY3', + 'location': 'CH0.BAY3', 'manufacturer': 'PURE', 'firmware': ''}] +port_data = [ + {'type': 'fc', 'name': 'CTO.FC1', 'native_port_id': 'CTO.FC1', + 'storage_id': '12345', 'location': 'CTO.FC1', + 'connection_status': 'disconnected', 'speed': 0, + 'health_status': 'normal', 'wwn': '43:dd:ff:45:gg:g4:rt:y', + 'mac_address': None, 'logical_type': 'management', + 'ipv4_mask': '100.12.253.23:4563', 'ipv4': '45233662jksndj'}, + {'type': 'eth', 'name': 'CTO.ETH15', 'native_port_id': 'CTO.ETH15', + 'storage_id': '12345', 'location': 'CTO.ETH15', + 'connection_status': 'connected', 'speed': 1000000, + 'health_status': 'normal', 'wwn': 'iqn.2016-11-01.com.pure', + 'mac_address': None, 'logical_type': 'management', + 'ipv4_mask': '100.12.253.23:4563', 'ipv4': '45233662jksndj'}] +initiator_data = [ + {'native_storage_host_initiator_id': 'iqn.1996-04.de.suse:01:ca9f3bcaf47', + 'native_storage_host_id': 'host', + 'name': 'iqn.1996-04.de.suse:01:ca9f3bcaf47', 'type': 'iscsi', + 'status': 'unknown', 'wwn': 'iqn.1996-04.de.suse:01:ca9f3bcaf47', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': 'iqn.1991-05.com.microsoft:win3', + 'native_storage_host_id': 'huhuitest', + 'name': 'iqn.1991-05.com.microsoft:win3', 'type': 'iscsi', + 'status': 'unknown', 'wwn': 'iqn.1991-05.com.microsoft:win3', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:2C:95:24', + 'native_storage_host_id': 'windows223', 'name': '21:00:00:24:FF:2C:95:24', + 'type': 'fc', 'status': 'unknown', 'wwn': '21:00:00:24:FF:2C:95:24', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:2C:95:25', + 'native_storage_host_id': 'windows223', 'name': '21:00:00:24:FF:2C:95:25', + 'type': 'fc', 'status': 'unknown', 'wwn': '21:00:00:24:FF:2C:95:25', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '10:00:00:00:C9:D5:BC:06', + 'native_storage_host_id': 'CL-B06-RH2288HV3-8-44-157-33', + 'name': '10:00:00:00:C9:D5:BC:06', 'type': 'fc', 'status': 'unknown', + 'wwn': '10:00:00:00:C9:D5:BC:06', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '10:00:00:00:C9:D5:BC:07', + 'native_storage_host_id': 'CL-B06-RH2288HV3-8-44-157-33', + 'name': '10:00:00:00:C9:D5:BC:07', 'type': 'fc', 'status': 'unknown', + 'wwn': '10:00:00:00:C9:D5:BC:07', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:76:D0:CC', + 'native_storage_host_id': 'CL-C21-RH5885HV3-8-44-165-22', + 'name': '21:00:00:24:FF:76:D0:CC', 'type': 'fc', 'status': 'unknown', + 'wwn': '21:00:00:24:FF:76:D0:CC', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:76:D0:CD', + 'native_storage_host_id': 'CL-C21-RH5885HV3-8-44-165-22', + 'name': '21:00:00:24:FF:76:D0:CD', 'type': 'fc', 'status': 'unknown', + 'wwn': '21:00:00:24:FF:76:D0:CD', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': 'iqn.1996-04.de.suse:01:66bf70288332', + 'native_storage_host_id': 'test-1s', + 'name': 'iqn.1996-04.de.suse:01:66bf70288332', 'type': 'iscsi', + 'status': 'unknown', 'wwn': 'iqn.1996-04.de.suse:01:66bf70288332', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:34:80:0D:6E:7A:DE', + 'native_storage_host_id': 'QIB', 'name': '21:00:34:80:0D:6E:7A:DE', + 'type': 'fc', 'status': 'unknown', 'wwn': '21:00:34:80:0D:6E:7A:DE', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:34:80:0D:6E:7A:DF', + 'native_storage_host_id': 'QIB', 'name': '21:00:34:80:0D:6E:7A:DF', + 'type': 'fc', 'status': 'unknown', 'wwn': '21:00:34:80:0D:6E:7A:DF', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '20:09:00:02:D2:93:7E:9F', + 'native_storage_host_id': 'v6-8-44-128-21', + 'name': '20:09:00:02:D2:93:7E:9F', 'type': 'fc', 'status': 'unknown', + 'wwn': '20:09:00:02:D2:93:7E:9F', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '20:19:00:02:D2:93:7E:9F', + 'native_storage_host_id': 'v6-8-44-128-21', + 'name': '20:19:00:02:D2:93:7E:9F', 'type': 'fc', 'status': 'unknown', + 'wwn': '20:19:00:02:D2:93:7E:9F', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': 'iqn.1994-05.com.redhat:1a9eaa70b558', + 'native_storage_host_id': 'host135', + 'name': 'iqn.1994-05.com.redhat:1a9eaa70b558', 'type': 'iscsi', + 'status': 'unknown', 'wwn': 'iqn.1994-05.com.redhat:1a9eaa70b558', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '22:00:CC:05:77:7C:3E:DF', + 'native_storage_host_id': 'zty-doradoV6', + 'name': '22:00:CC:05:77:7C:3E:DF', 'type': 'fc', 'status': 'unknown', + 'wwn': '22:00:CC:05:77:7C:3E:DF', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '22:10:CC:05:77:7C:3E:DF', + 'native_storage_host_id': 'zty-doradoV6', + 'name': '22:10:CC:05:77:7C:3E:DF', 'type': 'fc', 'status': 'unknown', + 'wwn': '22:10:CC:05:77:7C:3E:DF', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': 'iqn.1994-05.com.redhat:71cfb5b97df', + 'native_storage_host_id': 'CL-Test1', + 'name': 'iqn.1994-05.com.redhat:71cfb5b97df', 'type': 'iscsi', + 'status': 'unknown', 'wwn': 'iqn.1994-05.com.redhat:71cfb5b97df', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:76:D0:CF', + 'native_storage_host_id': 'CL-Test1', 'name': '21:00:00:24:FF:76:D0:CF', + 'type': 'fc', 'status': 'unknown', 'wwn': '21:00:00:24:FF:76:D0:CF', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': 'iqn.1994-05.com.redhat:80c412848b94', + 'native_storage_host_id': 'host137', + 'name': 'iqn.1994-05.com.redhat:80c412848b94', 'type': 'iscsi', + 'status': 'unknown', 'wwn': 'iqn.1994-05.com.redhat:80c412848b94', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:40:27:2A', + 'native_storage_host_id': 'zty-windows', + 'name': '21:00:00:24:FF:40:27:2A', 'type': 'fc', 'status': 'unknown', + 'wwn': '21:00:00:24:FF:40:27:2A', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:40:27:2B', + 'native_storage_host_id': 'zty-windows', + 'name': '21:00:00:24:FF:40:27:2B', 'type': 'fc', 'status': 'unknown', + 'wwn': '21:00:00:24:FF:40:27:2B', 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:53:51:F0', + 'native_storage_host_id': 'hswin41', 'name': '21:00:00:24:FF:53:51:F0', + 'type': 'fc', 'status': 'unknown', 'wwn': '21:00:00:24:FF:53:51:F0', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': '21:00:00:24:FF:53:51:F1', + 'native_storage_host_id': 'hswin41', 'name': '21:00:00:24:FF:53:51:F1', + 'type': 'fc', 'status': 'unknown', 'wwn': '21:00:00:24:FF:53:51:F1', + 'storage_id': '12345'}, + {'native_storage_host_initiator_id': 'nqn.2021-12.org.nvmexpress.mytest', + 'native_storage_host_id': 'zhilong-host0000002130', + 'name': 'nqn.2021-12.org.nvmexpress.mytest', 'type': 'nvme-of', + 'status': 'unknown', 'wwn': 'nqn.2021-12.org.nvmexpress.mytest', + 'storage_id': '12345'}] +host_data = [ + {'name': 'host', 'storage_id': '12345', 'native_storage_host_id': 'host', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'wxth', 'storage_id': '12345', 'native_storage_host_id': 'wxth', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'huhuitest', 'storage_id': '12345', + 'native_storage_host_id': 'huhuitest', 'os_type': 'Unknown', + 'status': 'normal'}, {'name': 'testGroup', 'storage_id': '12345', + 'native_storage_host_id': 'testGroup', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'windows223', 'storage_id': '12345', + 'native_storage_host_id': 'windows223', 'os_type': 'Unknown', + 'status': 'normal'}, + {'name': 'CL-B06-RH2288HV3-8-44-157-33', 'storage_id': '12345', + 'native_storage_host_id': 'CL-B06-RH2288HV3-8-44-157-33', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'CL-C21-RH5885HV3-8-44-165-22', 'storage_id': '12345', + 'native_storage_host_id': 'CL-C21-RH5885HV3-8-44-165-22', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'test-1s', 'storage_id': '12345', + 'native_storage_host_id': 'test-1s', 'os_type': 'Unknown', + 'status': 'normal'}, {'name': 'rhev125', 'storage_id': '12345', + 'native_storage_host_id': 'rhev125', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'QIB', 'storage_id': '12345', 'native_storage_host_id': 'QIB', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'v6-8-44-128-21', 'storage_id': '12345', + 'native_storage_host_id': 'v6-8-44-128-21', 'os_type': 'Unknown', + 'status': 'normal'}, {'name': 'host135', 'storage_id': '12345', + 'native_storage_host_id': 'host135', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'zty-doradoV6', 'storage_id': '12345', + 'native_storage_host_id': 'zty-doradoV6', 'os_type': 'Unknown', + 'status': 'normal'}, {'name': 'CL-Test1', 'storage_id': '12345', + 'native_storage_host_id': 'CL-Test1', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'host137', 'storage_id': '12345', + 'native_storage_host_id': 'host137', 'os_type': 'Unknown', + 'status': 'normal'}, {'name': 'hsesxi', 'storage_id': '12345', + 'native_storage_host_id': 'hsesxi', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'zty-windows', 'storage_id': '12345', + 'native_storage_host_id': 'zty-windows', 'os_type': 'Unknown', + 'status': 'normal'}, {'name': 'hosttest', 'storage_id': '12345', + 'native_storage_host_id': 'hosttest', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'hswin41', 'storage_id': '12345', + 'native_storage_host_id': 'hswin41', 'os_type': 'Unknown', + 'status': 'normal'}, {'name': 'ztj201', 'storage_id': '12345', + 'native_storage_host_id': 'ztj201', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'test123', 'storage_id': '12345', + 'native_storage_host_id': 'test123', 'os_type': 'Unknown', + 'status': 'normal'}, {'name': 'zsytest', 'storage_id': '12345', + 'native_storage_host_id': 'zsytest', + 'os_type': 'Unknown', 'status': 'normal'}, + {'name': 'zhilong-host0000002130', 'storage_id': '12345', + 'native_storage_host_id': 'zhilong-host0000002130', 'os_type': 'AIX', + 'status': 'normal'}] +host_group_data = { + 'storage_host_groups': + [ + {'native_storage_host_group_id': 'podgroup', 'name': 'podgroup', + 'storage_id': '12345'}, + {'native_storage_host_group_id': 'NewTest', 'name': 'NewTest', + 'storage_id': '12345'}, + {'native_storage_host_group_id': 'QIB', 'name': 'QIB', + 'storage_id': '12345'}, + {'native_storage_host_group_id': 'HGTest', 'name': 'HGTest', + 'storage_id': '12345'}], + 'storage_host_grp_host_rels': [ + {'native_storage_host_group_id': 'QIB', 'storage_id': '12345', + 'native_storage_host_id': 'QIB'}, + {'native_storage_host_group_id': 'HGTest', 'storage_id': '12345', + 'native_storage_host_id': 'host'}, + {'native_storage_host_group_id': 'HGTest', 'storage_id': '12345', + 'native_storage_host_id': 'hosttest'}] +} +volume_group_data = { + 'volume_groups': + [ + {'name': 'vvol-pure-VM1-072e131e-vg', 'storage_id': '12345', + 'native_volume_group_id': 'vvol-pure-VM1-072e131e-vg'}, + {'name': 'vvol-pure-vm2-e48a0ef8-vg', 'storage_id': '12345', + 'native_volume_group_id': 'vvol-pure-vm2-e48a0ef8-vg'}, + {'name': 'vvol-pure-vm3-65d42a4e-vg', 'storage_id': '12345', + 'native_volume_group_id': 'vvol-pure-vm3-65d42a4e-vg'}, + {'name': 'vvol-pure-vm4-17c41971-vg', 'storage_id': '12345', + 'native_volume_group_id': 'vvol-pure-vm4-17c41971-vg'}, + {'name': 'Volume-Group', 'storage_id': '12345', + 'native_volume_group_id': 'Volume-Group'}, + {'name': 'test1', 'storage_id': '12345', + 'native_volume_group_id': 'test1'}, + {'name': 'tangxuan', 'storage_id': '12345', + 'native_volume_group_id': 'tangxuan'} + ], + 'vol_grp_vol_rels': [ + {'storage_id': '12345', 'native_volume_group_id': 'Volume-Group', + 'native_volume_id': 'Volume-Group/voltest001'}, + {'storage_id': '12345', 'native_volume_group_id': 'Volume-Group', + 'native_volume_id': 'Volume-Group/voltest002'}, + {'storage_id': '12345', 'native_volume_group_id': 'Volume-Group', + 'native_volume_id': 'Volume-Group/voltest003'}, + {'storage_id': '12345', 'native_volume_group_id': 'Volume-Group', + 'native_volume_id': 'Volume-Group/voltest004'}, + {'storage_id': '12345', 'native_volume_group_id': 'Volume-Group', + 'native_volume_id': 'Volume-Group/voltest005'}] +} +views_data = [ + {'native_masking_view_id': 'QIBQIB1', 'name': 'QIBQIB1', + 'native_storage_host_group_id': 'QIB', 'native_volume_id': 'QIB1', + 'storage_id': '12345'}, + {'native_masking_view_id': 'QIBQIB2', 'name': 'QIBQIB2', + 'native_storage_host_group_id': 'QIB', 'native_volume_id': 'QIB2', + 'storage_id': '12345'}, + {'native_masking_view_id': 'HGTestVolume-Group/voltest005', + 'name': 'HGTestVolume-Group/voltest005', + 'native_storage_host_group_id': 'HGTest', + 'native_volume_id': 'Volume-Group/voltest005', 'storage_id': '12345'}, + {'native_masking_view_id': 'HGTestVolume-Group/voltest001', + 'name': 'HGTestVolume-Group/voltest001', + 'native_storage_host_group_id': 'HGTest', + 'native_volume_id': 'Volume-Group/voltest001', 'storage_id': '12345'}, + {'native_masking_view_id': 'HGTestVolume-Group/voltest002', + 'name': 'HGTestVolume-Group/voltest002', + 'native_storage_host_group_id': 'HGTest', + 'native_volume_id': 'Volume-Group/voltest002', 'storage_id': '12345'}, + {'native_masking_view_id': 'HGTestVolume-Group/voltest003', + 'name': 'HGTestVolume-Group/voltest003', + 'native_storage_host_group_id': 'HGTest', + 'native_volume_id': 'Volume-Group/voltest003', 'storage_id': '12345'}, + {'native_masking_view_id': 'HGTestVolume-Group/voltest004', + 'name': 'HGTestVolume-Group/voltest004', + 'native_storage_host_group_id': 'HGTest', + 'native_volume_id': 'Volume-Group/voltest004', 'storage_id': '12345'}, + {'native_masking_view_id': 'NewTesthomelab-pso-db_0000000002', + 'name': 'NewTesthomelab-pso-db_0000000002', + 'native_storage_host_group_id': 'NewTest', + 'native_volume_id': 'homelab-pso-db_0000000002', 'storage_id': '12345'}, + {'native_masking_view_id': 'zhilong-hgyzw_test0', + 'name': 'zhilong-hgyzw_test0', + 'native_storage_host_group_id': 'zhilong-hg', + 'native_volume_id': 'yzw_test0', 'storage_id': '12345'}, + {'native_masking_view_id': 'huhuitestNonehuhuitest', + 'name': 'huhuitestNonehuhuitest', 'native_storage_host_id': 'huhuitest', + 'native_volume_id': 'huhuitest', 'storage_id': '12345'}, + {'native_masking_view_id': 'wxthNonetest', 'name': 'wxthNonetest', + 'native_storage_host_id': 'wxth', 'native_volume_id': 'test', + 'storage_id': '12345'}, {'native_masking_view_id': 'testGroupNonetest', + 'name': 'testGroupNonetest', + 'native_storage_host_id': 'testGroup', + 'native_volume_id': 'test', + 'storage_id': '12345'}, + {'native_masking_view_id': 'windows223Nonewin2016_223', + 'name': 'windows223Nonewin2016_223', + 'native_storage_host_id': 'windows223', 'native_volume_id': 'win2016_223', + 'storage_id': '12345'}, { + 'native_masking_view_id': + 'CL-C21-RH5885HV3-8-44-165-22Nonepure-protocol-endpoint', + 'name': 'CL-C21-RH5885HV3-8-44-165-22Nonepure-protocol-endpoint', + 'native_storage_host_id': 'CL-C21-RH5885HV3-8-44-165-22', + 'native_volume_id': 'pure-protocol-endpoint', 'storage_id': '12345'}, { + 'native_masking_view_id': + 'CL-C21-RH5885HV3-8-44-165-22NoneCL_VOLUME_1_remote', + 'name': 'CL-C21-RH5885HV3-8-44-165-22NoneCL_VOLUME_1_remote', + 'native_storage_host_id': 'CL-C21-RH5885HV3-8-44-165-22', + 'native_volume_id': 'CL_VOLUME_1_remote', 'storage_id': '12345'}, + {'native_masking_view_id': 'test-1sNonelun-test1s', + 'name': 'test-1sNonelun-test1s', 'native_storage_host_id': 'test-1s', + 'native_volume_id': 'lun-test1s', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Noneyzw_iotest', + 'name': 'host135Noneyzw_iotest', 'native_storage_host_id': 'host135', + 'native_volume_id': 'yzw_iotest', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000003', + 'name': 'host137Nonehomelab-pso-db_0000000003', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000003', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000009', + 'name': 'host135Nonehomelab-pso-db_0000000009', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000009', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000012', + 'name': 'host135Nonehomelab-pso-db_0000000012', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000012', 'storage_id': '12345'}, + {'native_masking_view_id': 'v6-8-44-128-21Nonev6-8-44-128-21', + 'name': 'v6-8-44-128-21Nonev6-8-44-128-21', + 'native_storage_host_id': 'v6-8-44-128-21', + 'native_volume_id': 'v6-8-44-128-21', 'storage_id': '12345'}, + {'native_masking_view_id': 'v6-8-44-128-21NoneV6-8-44-128-21-002', + 'name': 'v6-8-44-128-21NoneV6-8-44-128-21-002', + 'native_storage_host_id': 'v6-8-44-128-21', + 'native_volume_id': 'V6-8-44-128-21-002', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000007', + 'name': 'host137Nonehomelab-pso-db_0000000007', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000007', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000010', + 'name': 'host135Nonehomelab-pso-db_0000000010', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000010', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000013', + 'name': 'host137Nonehomelab-pso-db_0000000013', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000013', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000000', + 'name': 'host135Nonehomelab-pso-db_0000000000', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000000', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000001', + 'name': 'host137Nonehomelab-pso-db_0000000001', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000001', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000016', + 'name': 'host137Nonehomelab-pso-db_0000000016', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000016', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000018', + 'name': 'host135Nonehomelab-pso-db_0000000018', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000018', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000015', + 'name': 'host135Nonehomelab-pso-db_0000000015', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000015', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000020', + 'name': 'host137Nonehomelab-pso-db_0000000020', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000020', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000021', + 'name': 'host135Nonehomelab-pso-db_0000000021', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000021', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000022', + 'name': 'host137Nonehomelab-pso-db_0000000022', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000022', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000019', + 'name': 'host135Nonehomelab-pso-db_0000000019', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000019', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000026', + 'name': 'host137Nonehomelab-pso-db_0000000026', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000026', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonehomelab-pso-db_0000000028', + 'name': 'host135Nonehomelab-pso-db_0000000028', + 'native_storage_host_id': 'host135', + 'native_volume_id': 'homelab-pso-db_0000000028', 'storage_id': '12345'}, + {'native_masking_view_id': 'host137Nonehomelab-pso-db_0000000024', + 'name': 'host137Nonehomelab-pso-db_0000000024', + 'native_storage_host_id': 'host137', + 'native_volume_id': 'homelab-pso-db_0000000024', 'storage_id': '12345'}, + {'native_masking_view_id': 'hsesxiNonehsboot', 'name': 'hsesxiNonehsboot', + 'native_storage_host_id': 'hsesxi', 'native_volume_id': 'hsboot', + 'storage_id': '12345'}, {'native_masking_view_id': 'hsesxiNonehszdata', + 'name': 'hsesxiNonehszdata', + 'native_storage_host_id': 'hsesxi', + 'native_volume_id': 'hszdata', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun16', + 'name': 'zty-doradoV6Nonezty_lun16', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun16', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun15', + 'name': 'zty-doradoV6Nonezty_lun15', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun15', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun13', + 'name': 'zty-doradoV6Nonezty_lun13', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun13', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun11', + 'name': 'zty-doradoV6Nonezty_lun11', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun11', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun14', + 'name': 'zty-doradoV6Nonezty_lun14', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun14', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun2', + 'name': 'zty-doradoV6Nonezty_lun2', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun2', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun5', + 'name': 'zty-doradoV6Nonezty_lun5', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun5', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun4', + 'name': 'zty-doradoV6Nonezty_lun4', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun4', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun1', + 'name': 'zty-doradoV6Nonezty_lun1', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun1', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun3', + 'name': 'zty-doradoV6Nonezty_lun3', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun3', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun6', + 'name': 'zty-doradoV6Nonezty_lun6', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun6', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun12', + 'name': 'zty-doradoV6Nonezty_lun12', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun12', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun10', + 'name': 'zty-doradoV6Nonezty_lun10', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun10', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun8', + 'name': 'zty-doradoV6Nonezty_lun8', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun8', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun7', + 'name': 'zty-doradoV6Nonezty_lun7', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun7', + 'storage_id': '12345'}, + {'native_masking_view_id': 'zty-doradoV6Nonezty_lun9', + 'name': 'zty-doradoV6Nonezty_lun9', + 'native_storage_host_id': 'zty-doradoV6', 'native_volume_id': 'zty_lun9', + 'storage_id': '12345'}, { + 'native_masking_view_id': + 'CL-B06-RH2288HV3-8-44-157-33Nonehomelab-pso-db_0000000001-u', + 'name': 'CL-B06-RH2288HV3-8-44-157-33Nonehomelab-pso-db_0000000001-u', + 'native_storage_host_id': 'CL-B06-RH2288HV3-8-44-157-33', + 'native_volume_id': 'homelab-pso-db_0000000001-u', + 'storage_id': '12345'}, { + 'native_masking_view_id': + 'CL-B06-RH2288HV3-8-44-157-33NoneVolume-Group/voltest001', + 'name': 'CL-B06-RH2288HV3-8-44-157-33NoneVolume-Group/voltest001', + 'native_storage_host_id': 'CL-B06-RH2288HV3-8-44-157-33', + 'native_volume_id': 'Volume-Group/voltest001', 'storage_id': '12345'}, + {'native_masking_view_id': 'zhilong-host0000002130Nonehswin4102', + 'name': 'zhilong-host0000002130Nonehswin4102', + 'native_storage_host_id': 'zhilong-host0000002130', + 'native_volume_id': 'hswin4102', 'storage_id': '12345'}, + {'native_masking_view_id': 'host135Nonetangxuan/tt001', + 'name': 'host135Nonetangxuan/tt001', 'native_storage_host_id': 'host135', + 'native_volume_id': 'tangxuan/tt001', 'storage_id': '12345'}, + {'native_masking_view_id': 'CL-Test1Nonehswin', + 'name': 'CL-Test1Nonehswin', 'native_storage_host_id': 'CL-Test1', + 'native_volume_id': 'hswin', 'storage_id': '12345'}, { + 'native_masking_view_id': + 'zhilong-host0000002130Nonehomelab-pso-db_0000000000-u', + 'name': 'zhilong-host0000002130Nonehomelab-pso-db_0000000000-u', + 'native_storage_host_id': 'zhilong-host0000002130', + 'native_volume_id': 'homelab-pso-db_0000000000-u', + 'storage_id': '12345'}, + {'native_masking_view_id': 'hosttestNonenc::136_connect', + 'name': 'hosttestNonenc::136_connect', + 'native_storage_host_id': 'hosttest', + 'native_volume_id': 'nc::136_connect', 'storage_id': '12345'}] def create_driver(): @@ -308,49 +1639,43 @@ def test_list_volumes(self): RestHandler.get_volumes = mock.Mock( side_effect=[volumes_info]) volume = self.driver.list_volumes(context) - self.assertEqual(volume[0]['native_volume_id'], - pool_info[0].get('volumes')[0]) + self.assertEqual(volume, volume_data) def test_get_storage(self): RestHandler.rest_call = mock.Mock( side_effect=[storage_info, hardware_info, drive_info, storage_id_info, controllers_info]) storage_object = self.driver.get_storage(context) - self.assertEqual(storage_object.get('name'), - storage_id_info.get('array_name')) + self.assertEqual(storage_object, storage_data) def test_list_alerts(self): RestHandler.rest_call = mock.Mock( side_effect=[alerts_info]) list_alerts = self.driver.list_alerts(context) - self.assertEqual(list_alerts[0].get('alert_id'), - alerts_info[0].get('id')) + self.assertEqual(list_alerts, list_alert_data) def test_parse_alert(self): parse_alert = self.driver.parse_alert(context, parse_alert_info) - self.assertEqual(parse_alert.get('alert_id'), - parse_alert_info.get('1.3.6.1.2.1.1.3.0')) + parse_alert_data['occur_time'] = parse_alert.get('occur_time') + self.assertDictEqual(parse_alert, parse_alert_data) def test_list_controllers(self): RestHandler.rest_call = mock.Mock( side_effect=[controllers_info, hardware_info]) list_controllers = self.driver.list_controllers(context) - self.assertEqual(list_controllers[0].get('name'), - controllers_info[0].get('name')) + self.assertListEqual(list_controllers, controllers_data) def test_list_disks(self): RestHandler.rest_call = mock.Mock( side_effect=[hardware_info, drive_info]) list_disks = self.driver.list_disks(context) - self.assertEqual(list_disks[0].get('name'), - drive_info[0].get('name')) + self.assertListEqual(list_disks, disk_data) def test_list_ports(self): RestHandler.rest_call = mock.Mock( side_effect=[port_network_info, port_info, hardware_info]) list_ports = self.driver.list_ports(context) - self.assertEqual(list_ports[0].get('name'), - hardware_info[0].get('name')) + self.assertListEqual(list_ports, port_data) def test_list_storage_pools(self): list_storage_pools = self.driver.list_storage_pools(context) @@ -366,3 +1691,33 @@ def test_reset_connection(self): LOG.error("test_reset_connection error: %s", six.text_type(e)) username = reset_connection_info.get('username') self.assertEqual(username, None) + + def test_list_storage_host_initiators(self): + RestHandler.rest_call = mock.Mock( + side_effect=[hosts_info]) + hosts = self.driver.list_storage_host_initiators(context) + self.assertEqual(hosts, initiator_data) + + def test_list_storage_hosts(self): + RestHandler.rest_call = mock.Mock( + side_effect=[HOSTS_PERSONALITY_INFO]) + hosts = self.driver.list_storage_hosts(context) + self.assertListEqual(hosts, host_data) + + def test_list_storage_host_groups(self): + RestHandler.rest_call = mock.Mock( + side_effect=[HGROUP_INFO]) + hgroup = self.driver.list_storage_host_groups(context) + self.assertDictEqual(hgroup, host_group_data) + + def test_list_volume_groups(self): + RestHandler.rest_call = mock.Mock( + side_effect=[VOLUME_GROUP_INFO]) + v_group = self.driver.list_volume_groups(context) + self.assertDictEqual(v_group, volume_group_data) + + def test_list_masking_views(self): + RestHandler.rest_call = mock.Mock( + side_effect=[HGROUP_CONNECT_INFO, HOSTS_CONNECT_INFO]) + views = self.driver.list_masking_views(context) + self.assertListEqual(views, views_data)