Repository: sodafoundation/delfin

Issue:
Update OceanStor driver with NAS resources support
**Issue/Feature Description:**
Update OceanStor driver with NAS resources support for Filesystem, Qtree, Share, Quota

**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/501?src=pr&el=h1) Report
> Merging [#501](https://codecov.io/gh/sodafoundation/delfin/pull/501?src=pr&el=desc) (cfb16d3) into [master](https://codecov.io/gh/sodafoundation/delfin/commit/8023a1f2b8273ed45bd0064c3cd0044feeeb8e0c?el=desc) (8023a1f) will **decrease** coverage by `0.71%`.
> The diff coverage is `63.56%`.


```diff
@@            Coverage Diff             @@
##           master     #501      +/-   ##
==========================================
- Coverage   67.95%   67.24%   -0.72%     
==========================================
  Files         113      119       +6     
  Lines        8217     8831     +614     
  Branches      926      996      +70     
==========================================
+ Hits         5584     5938     +354     
- Misses       2321     2552     +231     
- Partials      312      341      +29     
```

| [Impacted Files](https://codecov.io/gh/sodafoundation/delfin/pull/501?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [delfin/drivers/huawei/oceanstor/oceanstor.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2RyaXZlcnMvaHVhd2VpL29jZWFuc3Rvci9vY2VhbnN0b3IucHk=) | `67.05% <4.00%> (-26.32%)` | :arrow_down: |
| [delfin/drivers/huawei/oceanstor/rest\_client.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2RyaXZlcnMvaHVhd2VpL29jZWFuc3Rvci9yZXN0X2NsaWVudC5weQ==) | `66.05% <20.83%> (-5.74%)` | :arrow_down: |
| [delfin/task\_manager/tasks/resources.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL3Rhc2tfbWFuYWdlci90YXNrcy9yZXNvdXJjZXMucHk=) | `67.92% <39.74%> (-9.90%)` | :arrow_down: |
| [delfin/api/views/filesystems.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2FwaS92aWV3cy9maWxlc3lzdGVtcy5weQ==) | `42.85% <42.85%> (ø)` | |
| [delfin/api/views/qtrees.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2FwaS92aWV3cy9xdHJlZXMucHk=) | `42.85% <42.85%> (ø)` | |
| [delfin/api/views/shares.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2FwaS92aWV3cy9zaGFyZXMucHk=) | `42.85% <42.85%> (ø)` | |
| [delfin/api/v1/filesystems.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2FwaS92MS9maWxlc3lzdGVtcy5weQ==) | `52.00% <52.00%> (ø)` | |
| [delfin/api/v1/qtrees.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2FwaS92MS9xdHJlZXMucHk=) | `52.00% <52.00%> (ø)` | |
| [delfin/api/v1/shares.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2FwaS92MS9zaGFyZXMucHk=) | `52.00% <52.00%> (ø)` | |
| [delfin/drivers/fake\_storage/\_\_init\_\_.py](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree#diff-ZGVsZmluL2RyaXZlcnMvZmFrZV9zdG9yYWdlL19faW5pdF9fLnB5) | `76.07% <55.00%> (-5.75%)` | :arrow_down: |
| ... and [55 more](https://codecov.io/gh/sodafoundation/delfin/pull/501/diff?src=pr&el=tree-more) | |


Patch:
diff --git a/delfin/common/constants.py b/delfin/common/constants.py
index 7cb3722..b756db2 100644
--- a/delfin/common/constants.py
+++ b/delfin/common/constants.py
@@ -182,6 +182,13 @@ class QuotaType(object):
     ALL = (TREE, USER, GROUP)
 
 
+class FSType(object):
+    THICK = 'thick'
+    THIN = 'thin'
+
+    ALL = (THICK, THIN)
+
+
 class ShareProtocol(object):
     CIFS = 'cifs'
     NFS = 'nfs'
diff --git a/delfin/drivers/huawei/oceanstor/consts.py b/delfin/drivers/huawei/oceanstor/consts.py
index 731ee4a..899acc5 100644
--- a/delfin/drivers/huawei/oceanstor/consts.py
+++ b/delfin/drivers/huawei/oceanstor/consts.py
@@ -119,6 +119,28 @@ DiskLogicalTypeMap = {
         constants.DiskLogicalType.CACHE,
 }
 
+FS_WORM_COMPLIANCE = '1'
+FS_WORM_AUDIT_LOG = '2'
+FS_WORM_ENTERPRISE = '3'
+
+FS_HEALTH_NORMAL = '1'
+FS_TYPE_THICK = '0'
+FS_TYPE_THIN = '1'
+PARENT_TYPE_POOL = 216
+
+QUOTA_NOT_ENABLED = 'INVALID_VALUE64'
+QUOTA_TYPE_TREE = '1'
+QUOTA_TYPE_USER = '2'
+QUOTA_TYPE_GROUP = '3'
+
+SECURITY_STYLE_MIXED = '0'
+SECURITY_STYLE_NATIVE = '1'
+SECURITY_STYLE_NTFS = '2'
+SECURITY_STYLE_UNIX = '3'
+
+PARENT_OBJECT_TYPE_FS = 40
+SHARE_NFS = '16401'
+
 ERROR_CONNECT_TO_SERVER = -403
 ERROR_UNAUTHORIZED_TO_SERVER = -401
 
@@ -134,7 +156,7 @@ BLOCK_STORAGE_POOL_TYPE = '1'
 FILE_SYSTEM_POOL_TYPE = '2'
 
 SECTORS_SIZE = 512
-QUERY_PAGE_SIZE = 150
+QUERY_PAGE_SIZE = 100
 
 THICK_LUNTYPE = '0'
 THIN_LUNTYPE = '1'
diff --git a/delfin/drivers/huawei/oceanstor/oceanstor.py b/delfin/drivers/huawei/oceanstor/oceanstor.py
index b8783f6..d512574 100644
--- a/delfin/drivers/huawei/oceanstor/oceanstor.py
+++ b/delfin/drivers/huawei/oceanstor/oceanstor.py
@@ -354,6 +354,226 @@ class OceanStorDriver(driver.StorageDriver):
             LOG.error(err_msg)
             raise exception.InvalidResults(err_msg)
 
+    def _list_quotas(self, quotas, fs_id, qt_id):
+        q_type = {
+            consts.QUOTA_TYPE_TREE: constants.QuotaType.TREE,
+            consts.QUOTA_TYPE_USER: constants.QuotaType.USER,
+            consts.QUOTA_TYPE_GROUP: constants.QuotaType.GROUP,
+        }
+        q_list = []
+        for qt in quotas:
+            chq, csq, fhq, fsq = None, None, None, None
+            uc, fc = None, None
+            if qt['SPACEHARDQUOTA'] != consts.QUOTA_NOT_ENABLED:
+                chq = qt['SPACEHARDQUOTA']
+            if qt['SPACESOFTQUOTA'] != consts.QUOTA_NOT_ENABLED:
+                csq = qt['SPACESOFTQUOTA']
+            if qt['FILEHARDQUOTA'] != consts.QUOTA_NOT_ENABLED:
+                fhq = qt['FILEHARDQUOTA']
+            if qt['FILESOFTQUOTA'] != consts.QUOTA_NOT_ENABLED:
+                fsq = qt['FILESOFTQUOTA']
+            if qt['SPACEUSED'] != consts.QUOTA_NOT_ENABLED:
+                uc = qt['SPACEUSED']
+            if qt['FILEUSED'] != consts.QUOTA_NOT_ENABLED:
+                fc = qt['FILEUSED']
+            q = {
+                "native_quota_id": qt['ID'],
+                "type": q_type.get(qt['QUOTATYPE']),
+                "storage_id": self.storage_id,
+                "native_filesystem_id": fs_id,
+                "native_qtree_id": qt_id,
+                "capacity_hard_limit": chq,
+                "capacity_soft_limit": csq,
+                "file_hard_limit": fhq,
+                "file_soft_limit": fsq,
+                "file_count": fc,
+                "used_capacity": uc,
+                "user_group_name": qt['USRGRPOWNERNAME'],
+            }
+            q_list.append(q)
+            return q_list
+
+    def list_quotas(self, context):
+        try:
+            # Get list of OceanStor quotas details
+            quotas_list = []
+            filesystems = self.client.get_all_filesystems()
+            for fs in filesystems:
+                fs_id = fs["ID"]
+                quotas = self.client.get_all_filesystem_quotas(fs_id)
+                if quotas:
+                    qs = self._list_quotas(quotas, fs_id, None)
+                    quotas_list.extend(qs)
+
+            qtrees = self.client.get_all_qtrees(filesystems)
+            for qt in qtrees:
+                qt_id = qt["ID"]
+                quotas = self.client.get_all_qtree_quotas(qt_id)
+                if quotas:
+                    qs = self._list_quotas(quotas, None, qt_id)
+                    quotas_list.extend(qs)
+
+            return quotas_list
+
+        except exception.DelfinException as err:
+            err_msg = "Failed to get quotas from OceanStor: %s" %\
+                      (six.text_type(err))
+            LOG.error(err_msg)
+            raise err
+
+        except Exception as err:
+            err_msg = "Failed to get quotas from OceanStor: %s" %\
+                      (six.text_type(err))
+            LOG.error(err_msg)
+            raise exception.InvalidResults(err_msg)
+
+    def list_filesystems(self, context):
+        try:
+            # Get list of OceanStor filesystems details
+            fss = self.client.get_all_filesystems()
+
+            fs_list = []
+            worm_type = {
+                consts.FS_WORM_COMPLIANCE: constants.WORMType.COMPLIANCE,
+                consts.FS_WORM_AUDIT_LOG: constants.WORMType.AUDIT_LOG,
+                consts.FS_WORM_ENTERPRISE: constants.WORMType.ENTERPRISE
+            }
+            for fs in fss:
+                status = constants.FilesystemStatus.FAULTY
+                if fs['HEALTHSTATUS'] == consts.FS_HEALTH_NORMAL:
+                    status = constants.FilesystemStatus.NORMAL
+                fs_type = constants.FSType.THICK
+                if fs['ALLOCTYPE'] == consts.FS_TYPE_THIN:
+                    fs_type = constants.FSType.THIN
+
+                pool_id = None
+                if fs['PARENTTYPE'] == consts.PARENT_TYPE_POOL:
+                    pool_id = fs['PARENTID']
+
+                sector_size = int(fs['SECTORSIZE'])
+                total_cap = int(fs['CAPACITY']) * sector_size
+                used_cap = int(fs['ALLOCCAPACITY']) * sector_size
+                free_cap = int(fs['AVAILABLECAPCITY']) * sector_size
+
+                compressed = False
+                if fs['ENABLECOMPRESSION'] != 'false':
+                    compressed = True
+
+                deduplicated = False
+                if fs['ENABLEDEDUP'] != 'false':
+                    deduplicated = True
+
+                f = {
+                    'name': fs['NAME'],
+                    'storage_id': self.storage_id,
+                    'native_filesystem_id': fs['ID'],
+                    'native_pool_id': pool_id,
+                    'compressed': compressed,
+                    'deduplicated': deduplicated,
+                    'worm': worm_type.get(fs['WORMTYPE'],
+                                          constants.WORMType.NON_WORM),
+                    'status': status,
+                    'type': fs_type,
+                    'total_capacity': total_cap,
+                    'used_capacity': used_cap,
+                    'free_capacity': free_cap,
+                }
+                fs_list.append(f)
+
+            return fs_list
+
+        except exception.DelfinException as err:
+            err_msg = "Failed to get filesystems from OceanStor: %s" %\
+                      (six.text_type(err))
+            LOG.error(err_msg)
+            raise err
+
+        except Exception as err:
+            err_msg = "Failed to get filesystems from OceanStor: %s" %\
+                      (six.text_type(err))
+            LOG.error(err_msg)
+            raise exception.InvalidResults(err_msg)
+
+    def list_qtrees(self, context):
+        try:
+            # Get list of OceanStor qtrees details
+            filesystems = self.client.get_all_filesystems()
+            qts = self.client.get_all_qtrees(filesystems)
+            security_mode = {
+                consts.SECURITY_STYLE_MIXED: constants.NASSecurityMode.MIXED,
+                consts.SECURITY_STYLE_NATIVE: constants.NASSecurityMode.NATIVE,
+                consts.SECURITY_STYLE_NTFS: constants.NASSecurityMode.NTFS,
+                consts.SECURITY_STYLE_UNIX: constants.NASSecurityMode.UNIX,
+            }
+
+            qt_list = []
+            for qt in qts:
+                fs_id = None
+                if qt['PARENTTYPE'] == consts.PARENT_OBJECT_TYPE_FS:
+                    fs_id = qt['PARENTID']
+                q = {
+                    'name': qt['NAME'],
+                    'storage_id': self.storage_id,
+                    'native_qtree_id': qt['ID'],
+                    'native_filesystem_id': fs_id,
+                    'security_mode': security_mode.get(qt['securityStyle']),
+                }
+                qt_list.append(q)
+
+            return qt_list
+
+        except exception.DelfinException as err:
+            err_msg = "Failed to get qtrees from OceanStor: %s" %\
+                      (six.text_type(err))
+            LOG.error(err_msg)
+            raise err
+
+        except Exception as err:
+            err_msg = "Failed to get qtrees from OceanStor: %s" %\
+                      (six.text_type(err))
+            LOG.error(err_msg)
+            raise exception.InvalidResults(err_msg)
+
+    def list_shares(self, context):
+        try:
+            # Get list of OceanStor shares details
+            ss = self.client.get_all_shares()
+
+            s_list = []
+            for s in ss:
+
+                protocol = None
+                if s.get('type') == consts.SHARE_NFS:
+                    protocol = constants.ShareProtocol.NFS
+                if s.get('subType'):
+                    protocol = constants.ShareProtocol.CIFS
+                if s.get('ACCESSNAME'):
+                    protocol = constants.ShareProtocol.FTP
+
+                s = {
+                    'name': s['NAME'],
+                    'storage_id': self.storage_id,
+                    'native_share_id': s['ID'],
+                    'native_filesystem_id': s['FSID'],
+                    'path': s['SHAREPATH'],
+                    'protocol': protocol
+                }
+                s_list.append(s)
+
+            return s_list
+
+        except exception.DelfinException as err:
+            err_msg = "Failed to get shares from OceanStor: %s" %\
+                      (six.text_type(err))
+            LOG.error(err_msg)
+            raise err
+
+        except Exception as err:
+            err_msg = "Failed to get shares from OceanStor: %s" %\
+                      (six.text_type(err))
+            LOG.error(err_msg)
+            raise exception.InvalidResults(err_msg)
+
     def add_trap_config(self, context, trap_config):
         pass
 
diff --git a/delfin/drivers/huawei/oceanstor/rest_client.py b/delfin/drivers/huawei/oceanstor/rest_client.py
index 347c3a9..ab7f78e 100644
--- a/delfin/drivers/huawei/oceanstor/rest_client.py
+++ b/delfin/drivers/huawei/oceanstor/rest_client.py
@@ -204,13 +204,18 @@ class RestClient(object):
         return result
 
     def paginated_call(self, url, data=None, method=None,
-                       log_filter_flag=False,
+                       params=None, log_filter_flag=False,
                        page_size=consts.QUERY_PAGE_SIZE):
+        if params:
+            url = "{0}?{1}".format(url, params)
+        else:
+            url = "{0}?".format(url)
+
         result_list = []
         start, end = 0, page_size
         msg = _('Query resource volume error')
         while True:
-            url_p = '{0}?range=[{1}-{2}]'.format(url, start, end)
+            url_p = "{0}range=[{1}-{2}]".format(url, start, end)
             start, end = end, end + page_size
             result = self.call(url_p, data, method, log_filter_flag)
             self._assert_rest_result(result, msg)
@@ -306,6 +311,44 @@ class RestClient(object):
         url = "/storagepool"
         return self.paginated_call(url, None, "GET", log_filter_flag=True)
 
+    def get_all_filesystems(self):
+        url = "/filesystem"
+        return self.paginated_call(url, None, "GET", log_filter_flag=True)
+
+    def get_all_qtrees(self, filesystems):
+        url = "/quotatree"
+        qt_list = []
+        for fs in filesystems:
+            params = "PARENTTYPE=40&PARENTID={0}&".format(fs['ID'])
+            qt = self.paginated_call(url, None, "GET",
+                                     params=params, log_filter_flag=True)
+            qt_list.extend(qt)
+        return qt_list
+
+    def get_all_filesystem_quotas(self, fs_id):
+        url = "/FS_QUOTA"
+        params = "PARENTTYPE=40&PARENTID={0}&".format(fs_id)
+        return self.paginated_call(url, None, "GET",
+                                   params=params, log_filter_flag=True)
+
+    def get_all_qtree_quotas(self, qt_id):
+        url = "/FS_QUOTA"
+        params = "PARENTTYPE=16445&PARENTID={0}&".format(qt_id)
+        return self.paginated_call(url, None, "GET",
+                                   params=params, log_filter_flag=True)
+
+    def get_all_shares(self):
+        url = "/CIFSHARE"
+        cifs = self.paginated_call(url, None, "GET", log_filter_flag=True)
+
+        url = "/NFSHARE"
+        nfs = self.paginated_call(url, None, "GET", log_filter_flag=True)
+
+        url = "/FTP_SHARE_AUTH_CLIENT"
+        ftps = self.paginated_call(url, None, "GET", log_filter_flag=True)
+
+        return cifs + nfs + ftps
+
     def clear_alert(self, sequence_number):
         url = "/alarm/currentalarm?sequence=%s" % sequence_number
 


Test patch:
diff --git a/delfin/tests/unit/drivers/huawei/oceanstor/test_oceanstor.py b/delfin/tests/unit/drivers/huawei/oceanstor/test_oceanstor.py
index 7c64c59..e6b1bef 100644
--- a/delfin/tests/unit/drivers/huawei/oceanstor/test_oceanstor.py
+++ b/delfin/tests/unit/drivers/huawei/oceanstor/test_oceanstor.py
@@ -843,3 +843,312 @@ class TestOceanStorStorageDriver(TestCase):
                 driver.list_disks(context)
             self.assertIn('The results are invalid',
                           str(exc.exception))
+
+    def test_list_filesystems(self):
+        driver = create_driver()
+        expected = [
+            {
+                'name': 'fs1',
+                'storage_id': '12345',
+                'native_filesystem_id': '123',
+                'native_pool_id': '123',
+                'compressed': True,
+                'deduplicated': True,
+                'worm': 'non_worm',
+                'status': 'normal',
+                'type': 'thin',
+                'total_capacity': 81920,
+                'used_capacity': 8192,
+                'free_capacity': 8192,
+            },
+            {
+                'name': 'fs2',
+                'storage_id': '12345',
+                'native_filesystem_id': '123',
+                'native_pool_id': '123',
+                'compressed': False,
+                'deduplicated': False,
+                'worm': 'compliance',
+                'status': 'normal',
+                'type': 'thin',
+                'total_capacity': 81920,
+                'used_capacity': 81920,
+                'free_capacity': 8192,
+            },
+            {
+                'name': 'fs3',
+                'storage_id': '12345',
+                'native_filesystem_id': '123',
+                'native_pool_id': '123',
+                'compressed': True,
+                'deduplicated': True,
+                'worm': 'audit_log',
+                'status': 'normal',
+                'type': 'thin',
+                'total_capacity': 81920,
+                'used_capacity': 8192,
+                'free_capacity': 8192,
+            }
+
+        ]
+
+        ret = [
+            {
+                'data': [
+                    {
+                        'HEALTHSTATUS': '1',
+                        'ALLOCTYPE': '1',
+                        'SECTORSIZE': '8192',
+                        'CAPACITY': '10',
+                        'ALLOCCAPACITY': '1',
+                        'AVAILABLECAPCITY': '1',
+                        'ENABLECOMPRESSION': 'true',
+                        'ENABLEDEDUP': 'true',
+                        'NAME': 'fs1',
+                        'ID': '123',
+                        'PARENTTYPE': 216,
+                        'PARENTID': '123',
+                        'WORMTYPE': '0'
+                    },
+                    {
+                        'HEALTHSTATUS': '1',
+                        'ALLOCTYPE': '1',
+                        'SECTORSIZE': '8192',
+                        'CAPACITY': '10',
+                        'ALLOCCAPACITY': '10',
+                        'AVAILABLECAPCITY': '1',
+                        'ENABLECOMPRESSION': 'false',
+                        'ENABLEDEDUP': 'false',
+                        'NAME': 'fs2',
+                        'ID': '123',
+                        'PARENTTYPE': 216,
+                        'PARENTID': '123',
+                        'WORMTYPE': '1'
+                    },
+                    {
+                        'HEALTHSTATUS': '1',
+                        'ALLOCTYPE': '1',
+                        'SECTORSIZE': '8192',
+                        'CAPACITY': '10',
+                        'ALLOCCAPACITY': '1',
+                        'AVAILABLECAPCITY': '1',
+                        'ENABLECOMPRESSION': 'true',
+                        'ENABLEDEDUP': 'true',
+                        'NAME': 'fs3',
+                        'ID': '123',
+                        'PARENTTYPE': 216,
+                        'PARENTID': '123',
+                        'WORMTYPE': '2'
+                    }
+                ],
+                'error': {
+                    'code': 0,
+                    'description': '0'
+                }
+            },
+            {
+                'data': [{
+                    'SOFTVER': '1000',
+                }],
+                'error': {
+                    'code': 0,
+                    'description': '0'
+                }
+            }
+        ]
+        with mock.patch.object(RestClient, 'do_call', side_effect=ret):
+            fs = driver.list_filesystems(context)
+            self.assertDictEqual(fs[0], expected[0])
+            self.assertDictEqual(fs[1], expected[1])
+            self.assertDictEqual(fs[2], expected[2])
+
+        with mock.patch.object(RestClient, 'get_all_filesystems',
+                               side_effect=exception.DelfinException):
+            with self.assertRaises(Exception) as exc:
+                driver.list_filesystems(context)
+            self.assertIn('An unknown exception occurred',
+                          str(exc.exception))
+
+        with mock.patch.object(RestClient, 'get_all_filesystems',
+                               side_effect=TypeError):
+            with self.assertRaises(Exception) as exc:
+                driver.list_filesystems(context)
+            self.assertIn('The results are invalid',
+                          str(exc.exception))
+
+    def test_list_qtrees(self):
+        driver = create_driver()
+        expected = [
+            {
+                'name': 'qtree1',
+                'storage_id': '12345',
+                'native_qtree_id': '123',
+                'native_filesystem_id': '123',
+                'security_mode': 'mixed',
+            },
+            {
+                'name': 'WD00:1111',
+                'storage_id': '12345',
+                'native_disk_id': '0B',
+                'serial_number': '1111',
+                'manufacturer': 'WesterDigital',
+                'model': 'WD00',
+                'firmware': '123',
+                'speed': 10000,
+                'capacity': 5000000,
+                'status': 'offline',
+                'physical_type': 'ssd',
+                'logical_type': 'free',
+                'health_score': '255',
+                'native_disk_group_id': None,
+                'location': 'Location2',
+            },
+            {
+                'name': 'ST200:1234',
+                'storage_id': '12345',
+                'native_disk_id': '0A',
+                'serial_number': '1234',
+                'manufacturer': 'Segate',
+                'model': 'ST200',
+                'firmware': '0003',
+                'speed': 7200,
+                'capacity': 1000000,
+                'status': 'abnormal',
+                'physical_type': 'unknown',
+                'logical_type': 'free',
+                'health_score': '255',
+                'native_disk_group_id': None,
+                'location': 'Location1',
+            }
+
+        ]
+
+        ret = [
+            {
+                'data': [
+                    {
+                        'NAME': 'qtree1',
+                        'ID': '123',
+                        'securityStyle': '0',
+                        'PARENTTYPE': 40,
+                        'PARENTID': '123',
+                    },
+                ],
+                'error': {
+                    'code': 0,
+                    'description': '0'
+                }
+            }
+        ]
+        with mock.patch.object(RestClient, 'get_all_filesystems',
+                               side_effect=[[{"ID": "1"}]]):
+            with mock.patch.object(RestClient, 'do_call', side_effect=ret):
+                qtree = driver.list_qtrees(context)
+            self.assertDictEqual(qtree[0], expected[0])
+
+        with mock.patch.object(RestClient, 'get_all_filesystems',
+                               side_effect=exception.DelfinException):
+            with self.assertRaises(Exception) as exc:
+                driver.list_qtrees(context)
+            self.assertIn('An unknown exception occurred',
+                          str(exc.exception))
+
+        with mock.patch.object(RestClient, 'get_all_filesystems',
+                               side_effect=TypeError):
+            with self.assertRaises(Exception) as exc:
+                driver.list_qtrees(context)
+            self.assertIn('The results are invalid',
+                          str(exc.exception))
+
+    def test_list_shares(self):
+        driver = create_driver()
+        expected = [
+            {
+                'name': 'CIFS',
+                'storage_id': '12345',
+                'native_share_id': '111',
+                'native_filesystem_id': 'FS111',
+                'path': '/filesystem0001/',
+                'protocol': 'cifs'
+            },
+            {
+                'name': 'NFS',
+                'storage_id': '12345',
+                'native_share_id': '222',
+                'native_filesystem_id': 'FS222',
+                'path': '/filesystem0002/',
+                'protocol': 'nfs'
+            },
+            {
+                'name': 'FTP',
+                'storage_id': '12345',
+                'native_share_id': '333',
+                'native_filesystem_id': 'FS333',
+                'path': '/filesystem0003/',
+                'protocol': 'ftp'
+            }
+
+        ]
+
+        ret = [
+            {
+                'data': [
+                    {
+                        'subType': '0',
+                        'NAME': 'CIFS',
+                        'SHAREPATH': '/filesystem0001/',
+                        'ID': '111',
+                        'FSID': 'FS111'
+                    },
+                ],
+                'error': {
+                    'code': 0,
+                    'description': '0'
+                }
+            },
+            {
+                'data': [{
+                    'type': '16401',
+                    'NAME': 'NFS',
+                    'SHAREPATH': '/filesystem0002/',
+                    'ID': '222',
+                    'FSID': 'FS222'
+                }],
+                'error': {
+                    'code': 0,
+                    'description': '0'
+                }
+            },
+            {
+                'data': [{
+                    'ACCESSNAME': 'test',
+                    'NAME': 'FTP',
+                    'SHAREPATH': '/filesystem0003/',
+                    'ID': '333',
+                    'FSID': 'FS333'
+                }],
+                'error': {
+                    'code': 0,
+                    'description': '0'
+                }
+            }
+        ]
+        with mock.patch.object(RestClient, 'do_call', side_effect=ret):
+            share = driver.list_shares(context)
+            self.assertDictEqual(share[0], expected[0])
+            self.assertDictEqual(share[1], expected[1])
+            self.assertDictEqual(share[2], expected[2])
+
+        with mock.patch.object(RestClient, 'get_all_shares',
+                               side_effect=exception.DelfinException):
+            with self.assertRaises(Exception) as exc:
+                driver.list_shares(context)
+            self.assertIn('An unknown exception occurred',
+                          str(exc.exception))
+
+        with mock.patch.object(RestClient, 'get_all_shares',
+                               side_effect=TypeError):
+            with self.assertRaises(Exception) as exc:
+                driver.list_shares(context)
+            self.assertIn('The results are invalid',
+                          str(exc.exception))