Repository: sodafoundation/delfin

Issue:
EMC VMAX driver support for host mapping feature
**Issue/Feature Description:**
Delfin supports host mapping framework as part of Jerba release.
EMC VMAX driver need to add support for host mapping feature by implementing host mapping driver APIs

**Why this issue to fixed / feature is needed(give scenarios or use cases):**

**How to reproduce, in case of a bug:**

**Other Notes /  Environment Information: (Please give the env information, log link or any useful information for this issue)**



Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/sodafoundation/delfin/pull/803?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sodafoundation) Report
> Merging [#803](https://codecov.io/gh/sodafoundation/delfin/pull/803?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sodafoundation) (110c182) into [master](https://codecov.io/gh/sodafoundation/delfin/commit/6774a9a989a0383be72fa2775110a0336c35e53c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sodafoundation) (6774a9a) will **decrease** coverage by `0.47%`.
> The diff coverage is `14.90%`.


```diff
@@            Coverage Diff             @@
##           master     #803      +/-   ##
==========================================
- Coverage   70.54%   70.06%   -0.48%     
==========================================
  Files         180      180              
  Lines       18638    18799     +161     
  Branches     2749     2765      +16     
==========================================
+ Hits        13148    13172      +24     
- Misses       4646     4783     +137     
  Partials      844      844              
```

| [Impacted Files](https://codecov.io/gh/sodafoundation/delfin/pull/803?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sodafoundation) | Coverage Δ | |
|---|---|---|
| [delfin/drivers/dell\_emc/vmax/client.py](https://codecov.io/gh/sodafoundation/delfin/pull/803/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sodafoundation#diff-ZGVsZmluL2RyaXZlcnMvZGVsbF9lbWMvdm1heC9jbGllbnQucHk=) | `57.67% <6.74%> (-15.69%)` | :arrow_down: |
| [delfin/drivers/dell\_emc/vmax/rest.py](https://codecov.io/gh/sodafoundation/delfin/pull/803/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sodafoundation#diff-ZGVsZmluL2RyaXZlcnMvZGVsbF9lbWMvdm1heC9yZXN0LnB5) | `42.02% <20.00%> (-2.17%)` | :arrow_down: |
| [delfin/drivers/dell\_emc/vmax/vmax.py](https://codecov.io/gh/sodafoundation/delfin/pull/803/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=sodafoundation#diff-ZGVsZmluL2RyaXZlcnMvZGVsbF9lbWMvdm1heC92bWF4LnB5) | `76.19% <50.00%> (-4.37%)` | :arrow_down: |

sushanthakumar: LGTM
sushanthakumar: LGTM

Patch:
diff --git a/delfin/drivers/dell_emc/vmax/client.py b/delfin/drivers/dell_emc/vmax/client.py
index b5635e1..7b45c4e 100644
--- a/delfin/drivers/dell_emc/vmax/client.py
+++ b/delfin/drivers/dell_emc/vmax/client.py
@@ -427,6 +427,210 @@ class VMAXClient(object):
             LOG.error("Failed to get disk details from VMAX")
             raise
 
+    def list_storage_host_initiators(self, storage_id):
+        try:
+            # Get list of initiators
+            initiators = self.rest.get_initiator_list(self.array_id,
+                                                      self.uni_version)
+
+            initiator_list = []
+            for initiator in initiators:
+                initiator_info = self.rest.get_initiator(
+                    self.array_id, self.uni_version, initiator)
+                type_string = initiator_info.get('type', '').upper()
+                initiator_type = constants.InitiatorType.UNKNOWN
+                if 'FIBRE' in type_string:
+                    initiator_type = constants.InitiatorType.FC
+                if 'ISCSI' in type_string:
+                    initiator_type = constants.InitiatorType.ISCSI
+
+                initiator_status = constants.InitiatorStatus.ONLINE
+                if not initiator_info.get('on_fabric', False):
+                    initiator_status = constants.InitiatorStatus.OFFLINE
+
+                initiator_item = {
+                    'name': initiator,
+                    'storage_id': storage_id,
+                    'native_storage_host_initiator_id': initiator,
+                    'alias': initiator_info.get('alias'),
+                    'wwn': initiator_info.get('initiatorId'),
+                    'type': initiator_type,
+                    'status': initiator_status,
+                    'native_storage_host_id': initiator_info.get('host'),
+                }
+                initiator_list.append(initiator_item)
+            return initiator_list
+
+        except Exception:
+            LOG.error("Failed to get host initiator details from VMAX")
+            raise
+
+    def list_storage_hosts(self, storage_id):
+        try:
+            # Get list of storage hosts
+            hosts = self.rest.get_host_list(self.array_id,
+                                            self.uni_version)
+            host_list = []
+            for host in hosts:
+                host_info = self.rest.get_host(
+                    self.array_id, self.uni_version, host)
+
+                host_item = {
+                    'storage_id': storage_id,
+                    'native_storage_host_id': host_info.get('hostId'),
+                    'name': host_info.get('hostId'),
+                    'os_type': constants.HostOSTypes.UNKNOWN,
+                    'status': constants.HostStatus.NORMAL,
+                }
+                host_list.append(host_item)
+            return host_list
+
+        except Exception:
+            LOG.error("Failed to get storage host details from VMAX")
+            raise
+
+    def list_storage_host_groups(self, storage_id):
+        try:
+            # Get list of storage host groups
+            host_groups = self.rest.get_host_group_list(self.array_id,
+                                                        self.uni_version)
+            host_group_list = []
+            storage_host_grp_relation_list = []
+            for host_group in host_groups:
+                host_group_info = self.rest.get_host_group(
+                    self.array_id, self.uni_version, host_group)
+                host_group_item = {
+                    'name': host_group,
+                    'storage_id': storage_id,
+                    'native_storage_host_group_id': host_group,
+                }
+                host_group_list.append(host_group_item)
+
+                for storage_host in host_group_info['host']:
+                    storage_host_group_relation = {
+                        'storage_id': storage_id,
+                        'native_storage_host_group_id': host_group,
+                        'native_storage_host_id': storage_host.get('hostId')
+                    }
+                    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
+
+        except Exception:
+            LOG.error("Failed to get storage host group details from VMAX")
+            raise
+
+    def list_port_groups(self, storage_id):
+        try:
+            # Get list of port groups
+            port_groups = self.rest.get_port_group_list(self.array_id,
+                                                        self.uni_version)
+            port_group_list = []
+            port_group_relation_list = []
+            for port_group in port_groups:
+                port_group_info = self.rest.get_port_group(
+                    self.array_id, self.uni_version, port_group)
+                port_group_item = {
+                    'name': port_group,
+                    'storage_id': storage_id,
+                    'native_port_group_id': port_group,
+                }
+                port_group_list.append(port_group_item)
+
+                for port in port_group_info['symmetrixPortKey']:
+                    port_name = port['directorId'] + ':' + port['portId']
+                    port_group_relation = {
+                        'storage_id': storage_id,
+                        'native_port_group_id': port_group,
+                        'native_port_id': port_name
+                    }
+                    port_group_relation_list.append(port_group_relation)
+            result = {
+                'port_groups': port_group_list,
+                'port_grp_port_rels': port_group_relation_list
+            }
+            return result
+
+        except Exception:
+            LOG.error("Failed to get port group details from VMAX")
+            raise
+
+    def list_volume_groups(self, storage_id):
+        try:
+            # Get list of volume groups
+            volume_groups = self.rest.get_volume_group_list(self.array_id,
+                                                            self.uni_version)
+            volume_group_list = []
+            volume_group_relation_list = []
+            for volume_group in volume_groups:
+                # volume_group_info = self.rest.get_volume_group(
+                #     self.array_id, self.uni_version, volume_group)
+
+                volume_group_item = {
+                    'name': volume_group,
+                    'storage_id': storage_id,
+                    'native_volume_group_id': volume_group,
+                }
+                volume_group_list.append(volume_group_item)
+
+                # List all volumes except data volumes
+                volumes = self.rest.get_volume_list(
+                    self.array_id, version=self.uni_version,
+                    params={'data_volume': 'false',
+                            'storageGroupId': volume_group})
+                if not volumes:
+                    continue
+                for volume in volumes:
+                    volume_group_relation = {
+                        'storage_id': storage_id,
+                        'native_volume_group_id': volume_group,
+                        '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
+
+        except Exception:
+            LOG.error("Failed to get volume group details from VMAX")
+            raise
+
+    def list_masking_views(self, storage_id):
+        try:
+            # Get list of masking_views
+            masking_views = self.rest.get_masking_view_list(self.array_id,
+                                                            self.uni_version)
+            masking_view_list = []
+            for masking_view in masking_views:
+                mv_info = self.rest.get_masking_view(
+                    self.array_id, self.uni_version, masking_view)
+
+                masking_view_item = {
+                    'name': masking_view,
+                    'storage_id': storage_id,
+                    'native_masking_view_id': mv_info['maskingViewId'],
+                    'native_storage_host_id': mv_info.get('hostId'),
+                    'native_storage_host_group_id': mv_info.get(
+                        'hostGroupId'),
+                    'native_volume_group_id': mv_info.get('storageGroupId'),
+                    'native_port_group_id': mv_info.get('portGroupId'),
+                }
+                masking_view_list.append(masking_view_item)
+            return masking_view_list
+
+        except Exception:
+            LOG.error("Failed to get masking views details from VMAX")
+            raise
+
     def list_alerts(self, query_para):
         """Get all alerts from an array."""
         return self.rest.get_alerts(query_para, version=self.uni_version,
diff --git a/delfin/drivers/dell_emc/vmax/rest.py b/delfin/drivers/dell_emc/vmax/rest.py
index b7f27d1..a1e92cd 100644
--- a/delfin/drivers/dell_emc/vmax/rest.py
+++ b/delfin/drivers/dell_emc/vmax/rest.py
@@ -733,6 +733,203 @@ class VMaxRest(object):
             array, SYSTEM, 'disk', version=version, params=params)
         return disk_dict_list.get('disk_ids', [])
 
+    def get_initiator(self, array, version, initiator_id):
+        """Get a VMax initiator from array.
+        :param array: the array serial number
+        :param version: the unisphere version  -- int
+        :param initiator_id: the initiator id
+        :returns: initiator dict
+        :raises: StorageHostInitiatorNotFound
+        """
+        initiator_dict = self.get_resource(
+            array, SLOPROVISIONING, 'initiator', resource_name=initiator_id,
+            version=version)
+        if not initiator_dict:
+            exception_message = (_("Initiator %(initiator_id)s not found.")
+                                 % {'initiator_id': initiator_id})
+            LOG.error(exception_message)
+            raise exception.StorageHostInitiatorNotFound(initiator_id)
+        return initiator_dict
+
+    def get_initiator_list(self, array, version, params=None):
+        """Get a filtered list of VMax initiators from array.
+        Filter parameters are required as the unfiltered initiator list
+        could bevery large and could affect performance if called often.
+        :param array: the array serial number
+        :param version: the unisphere version
+        :param params: filter parameters
+        :returns: initiatorId -- list
+        """
+        initiator_dict_list = self.get_resource(
+            array, SLOPROVISIONING, 'initiator',
+            version=version, params=params)
+        return initiator_dict_list.get('initiatorId', [])
+
+    def get_host(self, array, version, host_id):
+        """Get a VMax host from array.
+        :param array: the array serial number
+        :param version: the unisphere version  -- int
+        :param host_id: the host id
+        :returns: host dict
+        :raises: StorageHostNotFound
+        """
+        host_dict = self.get_resource(
+            array, SLOPROVISIONING, 'host',
+            resource_name=host_id,
+            version=version)
+        if not host_dict:
+            exception_message = (_("Host %(host_id)s not found.")
+                                 % {'host_id': host_id})
+            LOG.error(exception_message)
+            raise exception.StorageHostNotFound(host_id)
+        return host_dict
+
+    def get_host_list(self, array, version, params=None):
+        """Get a filtered list of VMax hosts from array.
+        Filter parameters are required as the unfiltered host list
+        could bevery large and could affect performance if called often.
+        :param array: the array serial number
+        :param version: the unisphere version
+        :param params: filter parameters
+        :returns: hostId -- list
+        """
+        host_dict_list = self.get_resource(
+            array, SLOPROVISIONING, 'host',
+            version=version, params=params)
+        return host_dict_list.get('hostId', [])
+
+    def get_host_group(self, array, version, host_group_id):
+        """Get a VMax host group from array.
+        :param array: the array serial number
+        :param version: the unisphere version  -- int
+        :param host_group_id: the host group id
+        :returns: host group dict
+        :raises: StorageHostGroupNotFound
+        """
+        host_group_dict = self.get_resource(
+            array, SLOPROVISIONING, 'hostgroup',
+            resource_name=host_group_id,
+            version=version)
+        if not host_group_dict:
+            exception_message = (_("HostGroup %(host_group_id)s not found.")
+                                 % {'host_group_id': host_group_id})
+            LOG.error(exception_message)
+            raise exception.StorageHostGroupNotFound(host_group_id)
+        return host_group_dict
+
+    def get_host_group_list(self, array, version, params=None):
+        """Get a filtered list of VMax host groups from array.
+        Filter parameters are required as the unfiltered host list
+        could bevery large and could affect performance if called often.
+        :param array: the array serial number
+        :param version: the unisphere version
+        :param params: filter parameters
+        :returns: hostGroupId -- list
+        """
+        host_group_dict_list = self.get_resource(
+            array, SLOPROVISIONING, 'hostgroup',
+            version=version, params=params)
+        return host_group_dict_list.get('hostGroupId', [])
+
+    def get_port_group(self, array, version, port_group_id):
+        """Get a VMax port group from array.
+        :param array: the array serial number
+        :param version: the unisphere version  -- int
+        :param port_group_id: the port group id
+        :returns: port group dict
+        :raises: PortGroupNotFound
+        """
+        port_group_dict = self.get_resource(
+            array, SLOPROVISIONING, 'portgroup',
+            resource_name=port_group_id,
+            version=version)
+        if not port_group_dict:
+            exception_message = (_("PortGroup %(port_group_id)s not found.")
+                                 % {'port_group_id': port_group_id})
+            LOG.error(exception_message)
+            raise exception.PortGroupNotFound(port_group_id)
+        return port_group_dict
+
+    def get_port_group_list(self, array, version, params=None):
+        """Get a filtered list of VMax port groups from array.
+        Filter parameters are required as the unfiltered host list
+        could bevery large and could affect performance if called often.
+        :param array: the array serial number
+        :param version: the unisphere version
+        :param params: filter parameters
+        :returns: portGroupId -- list
+        """
+        port_group_dict_list = self.get_resource(
+            array, SLOPROVISIONING, 'portgroup',
+            version=version, params=params)
+        return port_group_dict_list.get('portGroupId', [])
+
+    def get_volume_group(self, array, version, storage_group_id):
+        """Get a VMax storage/volume group from array.
+        :param array: the array serial number
+        :param version: the unisphere version  -- int
+        :param storage_group_id: the storage group id
+        :returns: volume group dict
+        :raises: VolumeGroupNotFound
+        """
+        storage_group_dict = self.get_resource(
+            array, SLOPROVISIONING, 'storagegroup',
+            resource_name=storage_group_id,
+            version=version)
+        if not storage_group_dict:
+            exception_message = (_("StorageGroup %(sid)s not found.")
+                                 % {'id': storage_group_id})
+            LOG.error(exception_message)
+            raise exception.VolumeGroupNotFound(storage_group_id)
+        return storage_group_dict
+
+    def get_volume_group_list(self, array, version, params=None):
+        """Get a filtered list of VMax storage groups from array.
+        Filter parameters are required as the unfiltered host list
+        could bevery large and could affect performance if called often.
+        :param array: the array serial number
+        :param version: the unisphere version
+        :param params: filter parameters
+        :returns: storageGroupId -- list
+        """
+        storage_group_dict_list = self.get_resource(
+            array, SLOPROVISIONING, 'storagegroup',
+            version=version, params=params)
+        return storage_group_dict_list.get('storageGroupId', [])
+
+    def get_masking_view(self, array, version, masking_view_id):
+        """Get a VMax masking view from array.
+        :param array: the array serial number
+        :param version: the unisphere version  -- int
+        :param masking_view_id: the masking view id
+        :returns: masking view dict
+        :raises: MaskingViewNotFound
+        """
+        masking_view_dict = self.get_resource(
+            array, SLOPROVISIONING, 'maskingview',
+            resource_name=masking_view_id,
+            version=version)
+        if not masking_view_dict:
+            exception_message = (_("Masking View %(id)s not found.")
+                                 % {'id': masking_view_id})
+            LOG.error(exception_message)
+            raise exception.MaskingViewNotFound(masking_view_id)
+        return masking_view_dict
+
+    def get_masking_view_list(self, array, version, params=None):
+        """Get a filtered list of VMax masking views from array.
+        Filter parameters are required as the unfiltered initiator list
+        could bevery large and could affect performance if called often.
+        :param array: the array serial number
+        :param version: the unisphere version
+        :param params: filter parameters
+        :returns: maskingViewId -- list
+        """
+        masking_view_dict_list = self.get_resource(
+            array, SLOPROVISIONING, 'maskingview',
+            version=version, params=params)
+        return masking_view_dict_list.get('maskingViewId', [])
+
     def post_request(self, target_uri, payload):
         """Generate  a POST request.
         :param target_uri: the uri to query from unipshere REST API
diff --git a/delfin/drivers/dell_emc/vmax/vmax.py b/delfin/drivers/dell_emc/vmax/vmax.py
index f3cc716..c92f718 100644
--- a/delfin/drivers/dell_emc/vmax/vmax.py
+++ b/delfin/drivers/dell_emc/vmax/vmax.py
@@ -83,6 +83,24 @@ class VMAXStorageDriver(driver.StorageDriver):
     def list_disks(self, context):
         return self.client.list_disks(self.storage_id)
 
+    def list_storage_host_initiators(self, context):
+        return self.client.list_storage_host_initiators(self.storage_id)
+
+    def list_storage_hosts(self, context):
+        return self.client.list_storage_hosts(self.storage_id)
+
+    def list_storage_host_groups(self, context):
+        return self.client.list_storage_host_groups(self.storage_id)
+
+    def list_port_groups(self, context):
+        return self.client.list_port_groups(self.storage_id)
+
+    def list_volume_groups(self, context):
+        return self.client.list_volume_groups(self.storage_id)
+
+    def list_masking_views(self, context):
+        return self.client.list_masking_views(self.storage_id)
+
     def add_trap_config(self, context, trap_config):
         pass
 


Test patch:
diff --git a/delfin/tests/unit/drivers/dell_emc/vmax/test_vmax.py b/delfin/tests/unit/drivers/dell_emc/vmax/test_vmax.py
index 0668105..553cf4d 100644
--- a/delfin/tests/unit/drivers/dell_emc/vmax/test_vmax.py
+++ b/delfin/tests/unit/drivers/dell_emc/vmax/test_vmax.py
@@ -624,7 +624,6 @@ class TestVMAXStorageDriver(TestCase):
         self.assertEqual(driver.storage_id, "12345")
         self.assertEqual(driver.client.array_id, "00112233")
         ret = driver.list_disks(context)
-        print("return", ret)
         self.assertDictEqual(ret[0], expected[0])
         self.assertDictEqual(ret[1], expected[1])
 
@@ -644,6 +643,569 @@ class TestVMAXStorageDriver(TestCase):
         self.assertIn('Exception from Storage Backend',
                       str(exc.exception))
 
+    @mock.patch.object(VMaxRest, 'get_initiator')
+    @mock.patch.object(VMaxRest, 'get_initiator_list')
+    @mock.patch.object(VMaxRest, 'get_array_detail')
+    @mock.patch.object(VMaxRest, 'get_uni_version')
+    @mock.patch.object(VMaxRest, 'get_unisphere_version')
+    def test_list_storage_host_initiators(self, mock_unisphere_version,
+                                          mock_version, mock_array,
+                                          mock_initiators, mock_initiator):
+        expected = \
+            [
+                {
+                    'name': '1001',
+                    'storage_id': '12345',
+                    'native_storage_host_initiator_id': '1001',
+                    'alias': 'I1',
+                    'wwn': '1001',
+                    'type': 'fc',
+                    'status': 'online',
+                    'native_storage_host_id': 'host1',
+                },
+                {
+                    'name': '1002',
+                    'storage_id': '12345',
+                    'native_storage_host_initiator_id': '1002',
+                    'alias': 'I2',
+                    'wwn': '1002',
+                    'type': 'iscsi',
+                    'status': 'offline',
+                    'native_storage_host_id': 'host2',
+                },
+                {
+                    'name': '1003',
+                    'storage_id': '12345',
+                    'native_storage_host_initiator_id': '1003',
+                    'alias': 'I3',
+                    'wwn': '1003',
+                    'type': 'fc',
+                    'status': 'offline',
+                    'native_storage_host_id': 'host3',
+                }
+            ]
+        init_1 = {
+            'initiatorId': '1001',
+            'wwn': '1001',
+            'alias': 'I1',
+            'host': 'host1',
+            'on_fabric': True,
+            'type': 'FIBRE'
+        }
+        init_2 = {
+            'initiatorId': '1002',
+            'wwn': '1002',
+            'alias': 'I2',
+            'host': 'host2',
+            'type': 'ISCSI'
+        }
+        init_3 = {
+            'initiatorId': '1003',
+            'wwn': '1003',
+            'alias': 'I3',
+            'host': 'host3',
+            'type': 'FIBRE'
+        }
+
+        kwargs = VMAX_STORAGE_CONF
+        mock_version.return_value = ['V9.2.2.7', '92']
+        mock_unisphere_version.return_value = ['V9.2.2.7', '92']
+        mock_array.return_value = {'symmetrixId': ['00112233']}
+        mock_initiators.side_effect = [['1001', '1002', '1003']]
+        mock_initiator.side_effect = [init_1, init_2, init_3]
+
+        driver = VMAXStorageDriver(**kwargs)
+        self.assertEqual(driver.storage_id, "12345")
+        self.assertEqual(driver.client.array_id, "00112233")
+        ret = driver.list_storage_host_initiators(context)
+        self.assertDictEqual(ret[0], expected[0])
+        self.assertDictEqual(ret[1], expected[1])
+        self.assertDictEqual(ret[2], expected[2])
+
+        mock_initiators.side_effect = [['1001']]
+        mock_initiator.side_effect = [exception.StorageBackendException]
+        with self.assertRaises(Exception) as exc:
+            driver.list_storage_host_initiators(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+        mock_initiators.side_effect = [exception.StorageBackendException]
+        mock_initiator.side_effect = [init_1]
+        with self.assertRaises(Exception) as exc:
+            driver.list_storage_host_initiators(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+    @mock.patch.object(VMaxRest, 'get_host')
+    @mock.patch.object(VMaxRest, 'get_host_list')
+    @mock.patch.object(VMaxRest, 'get_array_detail')
+    @mock.patch.object(VMaxRest, 'get_uni_version')
+    @mock.patch.object(VMaxRest, 'get_unisphere_version')
+    def test_list_storage_hosts(self, mock_unisphere_version,
+                                mock_version, mock_array,
+                                mock_hosts, mock_host):
+        expected = \
+            [
+                {
+                    'storage_id': '12345',
+                    'name': 'h1',
+                    'native_storage_host_id': 'h1',
+                    'os_type': 'Unknown',
+                    'status': 'normal',
+                },
+                {
+                    'storage_id': '12345',
+                    'name': 'h2',
+                    'native_storage_host_id': 'h2',
+                    'os_type': 'Unknown',
+                    'status': 'normal',
+                },
+                {
+                    'storage_id': '12345',
+                    'name': 'h3',
+                    'native_storage_host_id': 'h3',
+                    'os_type': 'Unknown',
+                    'status': 'normal',
+                }
+            ]
+        host_1 = {
+            'hostId': 'h1',
+        }
+        host_2 = {
+            'hostId': 'h2',
+        }
+        host_3 = {
+            'hostId': 'h3',
+        }
+
+        kwargs = VMAX_STORAGE_CONF
+        mock_version.return_value = ['V9.2.2.7', '92']
+        mock_unisphere_version.return_value = ['V9.2.2.7', '92']
+        mock_array.return_value = {'symmetrixId': ['00112233']}
+        mock_hosts.side_effect = [['h1', 'h2', 'h3']]
+        mock_host.side_effect = [host_1, host_2, host_3]
+
+        driver = VMAXStorageDriver(**kwargs)
+        self.assertEqual(driver.storage_id, "12345")
+        self.assertEqual(driver.client.array_id, "00112233")
+        ret = driver.list_storage_hosts(context)
+        self.assertDictEqual(ret[0], expected[0])
+        self.assertDictEqual(ret[1], expected[1])
+        self.assertDictEqual(ret[2], expected[2])
+
+        mock_hosts.side_effect = [['h1']]
+        mock_host.side_effect = [exception.StorageBackendException]
+        with self.assertRaises(Exception) as exc:
+            driver.list_storage_hosts(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+        mock_hosts.side_effect = [exception.StorageBackendException]
+        mock_host.side_effect = [host_1]
+        with self.assertRaises(Exception) as exc:
+            driver.list_storage_hosts(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+    @mock.patch.object(VMaxRest, 'get_host_group')
+    @mock.patch.object(VMaxRest, 'get_host_group_list')
+    @mock.patch.object(VMaxRest, 'get_array_detail')
+    @mock.patch.object(VMaxRest, 'get_uni_version')
+    @mock.patch.object(VMaxRest, 'get_unisphere_version')
+    def test_list_storage_host_groups(self, mock_unisphere_version,
+                                      mock_version, mock_array,
+                                      mock_host_groups, mock_host_group):
+        expected = \
+            [
+                {
+                    'name': 'hg1',
+                    'storage_id': '12345',
+                    'native_storage_host_group_id': 'hg1',
+                },
+                {
+                    'name': 'hg2',
+                    'storage_id': '12345',
+                    'native_storage_host_group_id': 'hg2',
+                },
+                {
+                    'name': 'hg3',
+                    'storage_id': '12345',
+                    'native_storage_host_group_id': 'hg3',
+                }
+            ]
+        expected_rel = [
+            {
+                'storage_id': '12345',
+                'native_storage_host_group_id': 'hg1',
+                'native_storage_host_id': 'h1',
+            },
+            {
+                'storage_id': '12345',
+                'native_storage_host_group_id': 'hg1',
+                'native_storage_host_id': 'h2',
+            },
+            {
+                'storage_id': '12345',
+                'native_storage_host_group_id': 'hg2',
+                'native_storage_host_id': 'h2',
+            },
+            {
+                'storage_id': '12345',
+                'native_storage_host_group_id': 'hg3',
+                'native_storage_host_id': 'h1',
+            },
+        ]
+        hg_1 = {
+            'hostGroupId': 'hg1',
+            'host': [{'hostId': 'h1'}, {'hostId': 'h2'}],
+        }
+        hg_2 = {
+            'hostGroupId': 'hg2',
+            'host': [{'hostId': 'h2'}],
+        }
+        hg_3 = {
+            'hostGroupId': 'hg3',
+            'host': [{'hostId': 'h1'}],
+        }
+
+        kwargs = VMAX_STORAGE_CONF
+        mock_version.return_value = ['V9.2.2.7', '92']
+        mock_unisphere_version.return_value = ['V9.2.2.7', '92']
+        mock_array.return_value = {'symmetrixId': ['00112233']}
+        mock_host_groups.side_effect = [['hg1', 'hg2', 'hg3']]
+        mock_host_group.side_effect = [hg_1, hg_2, hg_3]
+
+        driver = VMAXStorageDriver(**kwargs)
+        self.assertEqual(driver.storage_id, "12345")
+        self.assertEqual(driver.client.array_id, "00112233")
+        ret = driver.list_storage_host_groups(context)
+        ret_hgs = ret['storage_host_groups']
+        ret_hg_rels = ret['storage_host_grp_host_rels']
+        self.assertDictEqual(ret_hgs[0], expected[0])
+        self.assertDictEqual(ret_hgs[1], expected[1])
+        self.assertDictEqual(ret_hgs[2], expected[2])
+        self.assertDictEqual(ret_hg_rels[0], expected_rel[0])
+        self.assertDictEqual(ret_hg_rels[1], expected_rel[1])
+        self.assertDictEqual(ret_hg_rels[2], expected_rel[2])
+        self.assertDictEqual(ret_hg_rels[3], expected_rel[3])
+
+        mock_host_groups.side_effect = [['hg1']]
+        mock_host_group.side_effect = [exception.StorageBackendException]
+        with self.assertRaises(Exception) as exc:
+            driver.list_storage_host_groups(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+        mock_host_groups.side_effect = [exception.StorageBackendException]
+        mock_host_group.side_effect = [hg_1]
+        with self.assertRaises(Exception) as exc:
+            driver.list_storage_host_groups(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+    @mock.patch.object(VMaxRest, 'get_port_group')
+    @mock.patch.object(VMaxRest, 'get_port_group_list')
+    @mock.patch.object(VMaxRest, 'get_array_detail')
+    @mock.patch.object(VMaxRest, 'get_uni_version')
+    @mock.patch.object(VMaxRest, 'get_unisphere_version')
+    def test_list_port_groups(self, mock_unisphere_version,
+                              mock_version, mock_array,
+                              mock_port_groups, mock_port_group):
+        expected = \
+            [
+                {
+                    'name': 'pg1',
+                    'storage_id': '12345',
+                    'native_port_group_id': 'pg1',
+                },
+                {
+                    'name': 'pg2',
+                    'storage_id': '12345',
+                    'native_port_group_id': 'pg2',
+                },
+                {
+                    'name': 'pg3',
+                    'storage_id': '12345',
+                    'native_port_group_id': 'pg3',
+                }
+            ]
+        expected_rel = [
+            {
+                'storage_id': '12345',
+                'native_port_group_id': 'pg1',
+                'native_port_id': 'FA-1D:1',
+            },
+            {
+                'storage_id': '12345',
+                'native_port_group_id': 'pg1',
+                'native_port_id': 'FA-1D:2',
+            },
+            {
+                'storage_id': '12345',
+                'native_port_group_id': 'pg2',
+                'native_port_id': 'FA-2D:2',
+            },
+            {
+                'storage_id': '12345',
+                'native_port_group_id': 'pg3',
+                'native_port_id': 'FA-3D:1',
+            },
+        ]
+        pg_1 = {
+            'hostGroupId': 'hg1',
+            'symmetrixPortKey': [
+                {
+                    "directorId": "FA-1D",
+                    "portId": "1"
+                },
+                {
+                    "directorId": "FA-1D",
+                    "portId": "2"
+                }
+            ],
+        }
+        pg_2 = {
+            'hostGroupId': 'hg2',
+            'symmetrixPortKey': [
+                {
+                    "directorId": "FA-2D",
+                    "portId": "2"
+                }
+            ],
+        }
+        pg_3 = {
+            'hostGroupId': 'hg3',
+            'symmetrixPortKey': [
+                {
+                    "directorId": "FA-3D",
+                    "portId": "1"
+                },
+            ],
+        }
+
+        kwargs = VMAX_STORAGE_CONF
+        mock_version.return_value = ['V9.2.2.7', '92']
+        mock_unisphere_version.return_value = ['V9.2.2.7', '92']
+        mock_array.return_value = {'symmetrixId': ['00112233']}
+        mock_port_groups.side_effect = [['pg1', 'pg2', 'pg3']]
+        mock_port_group.side_effect = [pg_1, pg_2, pg_3]
+
+        driver = VMAXStorageDriver(**kwargs)
+        self.assertEqual(driver.storage_id, "12345")
+        self.assertEqual(driver.client.array_id, "00112233")
+        ret = driver.list_port_groups(context)
+        ret_pgs = ret['port_groups']
+        ret_pg_rels = ret['port_grp_port_rels']
+        self.assertDictEqual(ret_pgs[0], expected[0])
+        self.assertDictEqual(ret_pgs[1], expected[1])
+        self.assertDictEqual(ret_pgs[2], expected[2])
+        self.assertDictEqual(ret_pg_rels[0], expected_rel[0])
+        self.assertDictEqual(ret_pg_rels[1], expected_rel[1])
+        self.assertDictEqual(ret_pg_rels[2], expected_rel[2])
+        self.assertDictEqual(ret_pg_rels[3], expected_rel[3])
+
+        mock_port_groups.side_effect = [['pg1']]
+        mock_port_group.side_effect = [exception.StorageBackendException]
+        with self.assertRaises(Exception) as exc:
+            driver.list_port_groups(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+        mock_port_groups.side_effect = [exception.StorageBackendException]
+        mock_port_group.side_effect = [pg_1]
+        with self.assertRaises(Exception) as exc:
+            driver.list_port_groups(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+    @mock.patch.object(VMaxRest, 'get_volume_list')
+    @mock.patch.object(VMaxRest, 'get_volume_group_list')
+    @mock.patch.object(VMaxRest, 'get_array_detail')
+    @mock.patch.object(VMaxRest, 'get_uni_version')
+    @mock.patch.object(VMaxRest, 'get_unisphere_version')
+    def test_list_volume_groups(self, mock_unisphere_version,
+                                mock_version, mock_array,
+                                mock_volume_groups, mock_volumes):
+        expected = \
+            [
+                {
+                    'name': 'vg1',
+                    'storage_id': '12345',
+                    'native_volume_group_id': 'vg1',
+                },
+                {
+                    'name': 'vg2',
+                    'storage_id': '12345',
+                    'native_volume_group_id': 'vg2',
+                },
+                {
+                    'name': 'vg3',
+                    'storage_id': '12345',
+                    'native_volume_group_id': 'vg3',
+                }
+            ]
+        expected_rel = [
+            {
+                'storage_id': '12345',
+                'native_volume_group_id': 'vg1',
+                'native_volume_id': 'volume1',
+            },
+            {
+                'storage_id': '12345',
+                'native_volume_group_id': 'vg1',
+                'native_volume_id': 'volume2',
+            },
+            {
+                'storage_id': '12345',
+                'native_volume_group_id': 'vg2',
+                'native_volume_id': 'volume2',
+            },
+            {
+                'storage_id': '12345',
+                'native_volume_group_id': 'vg3',
+                'native_volume_id': 'volume1',
+            },
+        ]
+        v_1 = ['volume1', 'volume2']
+        v_2 = ['volume2']
+        v_3 = ['volume1']
+
+        kwargs = VMAX_STORAGE_CONF
+        mock_version.return_value = ['V9.2.2.7', '92']
+        mock_unisphere_version.return_value = ['V9.2.2.7', '92']
+        mock_array.return_value = {'symmetrixId': ['00112233']}
+        mock_volume_groups.side_effect = [['vg1', 'vg2', 'vg3']]
+        mock_volumes.side_effect = [v_1, v_2, v_3]
+
+        driver = VMAXStorageDriver(**kwargs)
+        self.assertEqual(driver.storage_id, "12345")
+        self.assertEqual(driver.client.array_id, "00112233")
+        ret = driver.list_volume_groups(context)
+        ret_vgs = ret['volume_groups']
+        ret_vg_rels = ret['vol_grp_vol_rels']
+        self.assertDictEqual(ret_vgs[0], expected[0])
+        self.assertDictEqual(ret_vgs[1], expected[1])
+        self.assertDictEqual(ret_vgs[2], expected[2])
+        self.assertDictEqual(ret_vg_rels[0], expected_rel[0])
+        self.assertDictEqual(ret_vg_rels[1], expected_rel[1])
+        self.assertDictEqual(ret_vg_rels[2], expected_rel[2])
+        self.assertDictEqual(ret_vg_rels[3], expected_rel[3])
+
+        mock_volume_groups.side_effect = [['vg1']]
+        mock_volumes.side_effect = [exception.StorageBackendException]
+        with self.assertRaises(Exception) as exc:
+            driver.list_volume_groups(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+        mock_volume_groups.side_effect = [exception.StorageBackendException]
+        mock_volumes.side_effect = [v_1]
+        with self.assertRaises(Exception) as exc:
+            driver.list_volume_groups(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+    @mock.patch.object(VMaxRest, 'get_masking_view')
+    @mock.patch.object(VMaxRest, 'get_masking_view_list')
+    @mock.patch.object(VMaxRest, 'get_array_detail')
+    @mock.patch.object(VMaxRest, 'get_uni_version')
+    @mock.patch.object(VMaxRest, 'get_unisphere_version')
+    def test_list_masking_views(self, mock_unisphere_version,
+                                mock_version, mock_array,
+                                mock_masking_views, mock_masking_view):
+        expected = \
+            [
+                {
+                    'storage_id': '12345',
+                    'native_storage_host_id': 'host1',
+                    'native_storage_host_group_id': 'hg1',
+                    'native_volume_group_id': 'sg1',
+                    'native_port_group_id': 'pg1',
+                    'native_masking_view_id': 'mv1',
+                    'name': 'mv1',
+                },
+                {
+                    'storage_id': '12345',
+                    'native_storage_host_id': 'host2',
+                    'native_storage_host_group_id': 'hg2',
+                    'native_volume_group_id': 'sg2',
+                    'native_port_group_id': 'pg2',
+                    'native_masking_view_id': 'mv2',
+                    'name': 'mv2',
+                },
+                {
+                    'storage_id': '12345',
+                    'native_storage_host_id': 'host3',
+                    'native_storage_host_group_id': 'hg3',
+                    'native_volume_group_id': 'sg3',
+                    'native_port_group_id': 'pg3',
+                    'native_masking_view_id': 'mv3',
+                    'name': 'mv3',
+                }
+            ]
+        mv_1 = {
+            'maskingViewId': 'mv1',
+            'hostId': 'host1',
+            'hostGroupId': 'hg1',
+            'storageGroupId': 'sg1',
+            'portGroupId': 'pg1',
+        }
+        mv_2 = {
+            'maskingViewId': 'mv2',
+            'hostId': 'host2',
+            'hostGroupId': 'hg2',
+            'storageGroupId': 'sg2',
+            'portGroupId': 'pg2',
+        }
+        mv_3 = {
+            'maskingViewId': 'mv3',
+            'hostId': 'host3',
+            'hostGroupId': 'hg3',
+            'storageGroupId': 'sg3',
+            'portGroupId': 'pg3',
+        }
+
+        kwargs = VMAX_STORAGE_CONF
+        mock_version.return_value = ['V9.2.2.7', '92']
+        mock_unisphere_version.return_value = ['V9.2.2.7', '92']
+        mock_array.return_value = {'symmetrixId': ['00112233']}
+        mock_masking_views.side_effect = [['mv1', 'mv2', 'mv3']]
+        mock_masking_view.side_effect = [mv_1, mv_2, mv_3]
+
+        driver = VMAXStorageDriver(**kwargs)
+        self.assertEqual(driver.storage_id, "12345")
+        self.assertEqual(driver.client.array_id, "00112233")
+        ret = driver.list_masking_views(context)
+        self.assertDictEqual(ret[0], expected[0])
+        self.assertDictEqual(ret[1], expected[1])
+        self.assertDictEqual(ret[2], expected[2])
+
+        mock_masking_views.side_effect = [['mv1']]
+        mock_masking_view.side_effect = [exception.StorageBackendException]
+        with self.assertRaises(Exception) as exc:
+            driver.list_masking_views(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
+        mock_masking_views.side_effect = [exception.StorageBackendException]
+        mock_masking_view.side_effect = [mv_1]
+        with self.assertRaises(Exception) as exc:
+            driver.list_masking_views(context)
+
+        self.assertIn('Exception from Storage Backend',
+                      str(exc.exception))
+
     @mock.patch.object(Session, 'request')
     @mock.patch.object(VMaxRest, 'get_array_detail')
     @mock.patch.object(VMaxRest, 'get_uni_version')