From b3bcba03056a6285bbe5971acd71a1f6d619f74d Mon Sep 17 00:00:00 2001 From: Joseph Vazhappilly Date: Fri, 18 Dec 2020 16:11:02 +0530 Subject: [PATCH] Add Disk resource to Delfin (#407) * Add Disk resource to Delfin * Add unit tests of disk db apis * Fix review comments --- delfin/api/v1/disks.py | 54 +++++++ delfin/api/v1/router.py | 5 + delfin/api/views/disks.py | 26 ++++ delfin/common/constants.py | 28 ++++ delfin/db/api.py | 22 ++- delfin/db/sqlalchemy/api.py | 134 +++++++++++++++++- delfin/db/sqlalchemy/models.py | 15 +- delfin/drivers/api.py | 5 + delfin/drivers/dell_emc/vmax/vmax.py | 3 + delfin/drivers/driver.py | 5 + delfin/drivers/fake_storage/__init__.py | 36 +++++ delfin/drivers/hpe/hpe_3par/hpe_3parstor.py | 3 + delfin/drivers/huawei/oceanstor/oceanstor.py | 3 + delfin/exception.py | 4 + delfin/task_manager/tasks/resources.py | 50 +++++++ delfin/tests/unit/db/test_db_api.py | 62 ++++++++ delfin/tests/unit/drivers/test_api.py | 23 ++- .../tests/unit/task_manager/test_resources.py | 66 ++++++++- 18 files changed, 532 insertions(+), 12 deletions(-) create mode 100644 delfin/api/v1/disks.py create mode 100644 delfin/api/views/disks.py diff --git a/delfin/api/v1/disks.py b/delfin/api/v1/disks.py new file mode 100644 index 000000000..87eb61360 --- /dev/null +++ b/delfin/api/v1/disks.py @@ -0,0 +1,54 @@ +# Copyright 2020 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 import db +from delfin.api import api_utils +from delfin.api.common import wsgi +from delfin.api.views import disks as disk_view + + +class DiskController(wsgi.Controller): + + def __init__(self): + super(DiskController, self).__init__() + self.search_options = ['name', 'status', 'id', 'storage_id', + 'native_disk_id'] + + def _get_disks_search_options(self): + """Return disks search options allowed .""" + return self.search_options + + def index(self, req): + ctxt = req.environ['delfin.context'] + query_params = {} + query_params.update(req.GET) + # update options other than filters + sort_keys, sort_dirs = api_utils.get_sort_params(query_params) + marker, limit, offset = api_utils.get_pagination_params(query_params) + # strip out options except supported search options + api_utils.remove_invalid_options(ctxt, query_params, + self._get_disks_search_options()) + + disks = db.disk_get_all(ctxt, marker, limit, sort_keys, + sort_dirs, query_params, offset) + return disk_view.build_disks(disks) + + def show(self, req, id): + ctxt = req.environ['delfin.context'] + disk = db.disk_get(ctxt, id) + return disk_view.build_disk(disk) + + +def create_resource(): + return wsgi.Resource(DiskController()) diff --git a/delfin/api/v1/router.py b/delfin/api/v1/router.py index 1c16e2ae5..4f5bc1132 100644 --- a/delfin/api/v1/router.py +++ b/delfin/api/v1/router.py @@ -19,6 +19,7 @@ from delfin.api.v1 import alerts from delfin.api.v1 import controllers from delfin.api.v1 import ports +from delfin.api.v1 import disks from delfin.api.v1 import storage_pools from delfin.api.v1 import storages from delfin.api.v1 import volumes @@ -104,3 +105,7 @@ def _setup_routes(self, mapper): self.resources['ports'] = ports.create_resource() mapper.resource("port", "ports", controller=self.resources['ports']) + + self.resources['disks'] = disks.create_resource() + mapper.resource("disk", "disks", + controller=self.resources['disks']) diff --git a/delfin/api/views/disks.py b/delfin/api/views/disks.py new file mode 100644 index 000000000..a75331e9c --- /dev/null +++ b/delfin/api/views/disks.py @@ -0,0 +1,26 @@ +# Copyright 2020 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. +import copy + + +def build_disks(disks): + # Build list of disks + views = [build_disk(disk) + for disk in disks] + return dict(disks=views) + + +def build_disk(disk): + view = copy.deepcopy(disk) + return dict(view) diff --git a/delfin/common/constants.py b/delfin/common/constants.py index bbd7831fd..49f73483f 100644 --- a/delfin/common/constants.py +++ b/delfin/common/constants.py @@ -120,6 +120,34 @@ class PortLogicalType(object): INTERNAL, MAINTENANCE, INTERCONNECT, OTHER) +class DiskStatus(object): + NORMAL = 'normal' + ABNORMAL = 'abnormal' + OFFLINE = 'offline' + + ALL = (NORMAL, ABNORMAL, OFFLINE) + + +class DiskPhysicalType(object): + SATA = 'sata' + SAS = 'sas' + SSD = 'ssd' + NL_SSD = 'nl-ssd' + UNKNOWN = 'unknown' + + ALL = (SATA, SAS, SSD, NL_SSD, UNKNOWN) + + +class DiskLogicalType(object): + FREE = 'free' + MEMBER = 'member' + HOTSPARE = 'hotspare' + CACHE = 'cache' + UNKNOWN = 'unknown' + + ALL = (FREE, MEMBER, HOTSPARE, CACHE, UNKNOWN) + + # Enumerations for alert severity class Severity(object): FATAL = 'Fatal' diff --git a/delfin/db/api.py b/delfin/db/api.py index 31bf9e3a9..6171a5396 100644 --- a/delfin/db/api.py +++ b/delfin/db/api.py @@ -344,6 +344,21 @@ def port_get_all(context, marker=None, limit=None, sort_keys=None, sort_dirs, filters, offset) +def disks_create(context, values): + """Create multiple disks.""" + return IMPL.disks_create(context, values) + + +def disks_update(context, values): + """Update multiple disks.""" + return IMPL.disks_update(context, values) + + +def disks_delete(context, values): + """Delete multiple disks.""" + return IMPL.disks_delete(context, values) + + def disk_create(context, values): """Create a disk from the values dictionary.""" return IMPL.disk_create(context, values) @@ -351,7 +366,7 @@ def disk_create(context, values): def disk_update(context, disk_id, values): """Update a disk withe the values dictionary.""" - return IMPL.disk_create(context, disk_id, values) + return IMPL.disk_update(context, disk_id, values) def disk_get(context, disk_id): @@ -359,6 +374,11 @@ def disk_get(context, disk_id): return IMPL.disk_get(context, disk_id) +def disk_delete_by_storage(context, storage_id): + """Delete a disk or raise an exception if it does not exist.""" + return IMPL.disk_delete_by_storage(context, storage_id) + + def disk_get_all(context, marker=None, limit=None, sort_keys=None, sort_dirs=None, filters=None, offset=None): """Retrieves all disks. diff --git a/delfin/db/sqlalchemy/api.py b/delfin/db/sqlalchemy/api.py index 2a2a06301..5e7a3a336 100644 --- a/delfin/db/sqlalchemy/api.py +++ b/delfin/db/sqlalchemy/api.py @@ -867,25 +867,147 @@ def _process_port_info_filters(query, filters): return query +def disks_create(context, disks): + """Create multiple disks.""" + session = get_session() + disks_refs = [] + with session.begin(): + + for disk in disks: + LOG.debug('adding new disk for native_disk_id {0}:' + .format(disk.get('native_disk_id'))) + if not disk.get('id'): + disk['id'] = uuidutils.generate_uuid() + + disk_ref = models.Disk() + disk_ref.update(disk) + disks_refs.append(disk_ref) + + session.add_all(disks_refs) + + return disks_refs + + +def disks_update(context, disks): + """Update multiple disks.""" + session = get_session() + + with session.begin(): + disk_refs = [] + + for disk in disks: + LOG.debug('updating disk {0}:'.format( + disk.get('id'))) + query = _disk_get_query(context, session) + result = query.filter_by(id=disk.get('id') + ).update(disk) + + if not result: + LOG.error(exception.DiskNotFound(disk.get( + 'id'))) + else: + disk_refs.append(result) + + return disk_refs + + +def disks_delete(context, disks_id_list): + """Delete multiple disks.""" + session = get_session() + with session.begin(): + for disk_id in disks_id_list: + LOG.debug('deleting disk {0}:'.format(disk_id)) + query = _disk_get_query(context, session) + result = query.filter_by(id=disk_id).delete() + + if not result: + LOG.error(exception.DiskNotFound(disk_id)) + + return + + +def _disk_get_query(context, session=None): + return model_query(context, models.Disk, session=session) + + +def _disk_get(context, disk_id, session=None): + result = (_disk_get_query(context, session=session) + .filter_by(id=disk_id) + .first()) + + if not result: + raise exception.DiskNotFound(disk_id) + + return result + + def disk_create(context, values): """Create a disk from the values dictionary.""" - return NotImplemented + if not values.get('id'): + values['id'] = uuidutils.generate_uuid() + + disk_ref = models.Disk() + disk_ref.update(values) + + session = get_session() + with session.begin(): + session.add(disk_ref) + + return _disk_get(context, + disk_ref['id'], + session=session) def disk_update(context, disk_id, values): - """Update a disk withe the values dictionary.""" - return NotImplemented + """Update a disk with the values dictionary.""" + session = get_session() + + with session.begin(): + query = _disk_get_query(context, session) + result = query.filter_by(id=disk_id).update(values) + + if not result: + raise exception.DiskNotFound(disk_id) + + return result def disk_get(context, disk_id): """Get a disk or raise an exception if it does not exist.""" - return NotImplemented + return _disk_get(context, disk_id) + + +def disk_delete_by_storage(context, storage_id): + """Delete disk or raise an exception if it does not exist.""" + _disk_get_query(context).filter_by(storage_id=storage_id).delete() def disk_get_all(context, marker=None, limit=None, sort_keys=None, sort_dirs=None, filters=None, offset=None): """Retrieves all disks.""" - return NotImplemented + + session = get_session() + with session.begin(): + # Generate the query + query = _generate_paginate_query(context, session, models.Disk, + marker, limit, sort_keys, sort_dirs, + filters, offset, + ) + # No Disk would match, return empty list + if query is None: + return [] + return query.all() + + +@apply_like_filters(model=models.Disk) +def _process_disk_info_filters(query, filters): + """Common filter processing for disks queries.""" + if filters: + if not is_valid_model_filters(models.Disk, filters): + return + query = query.filter_by(**filters) + + return query def is_orm_value(obj): @@ -1002,6 +1124,8 @@ def alert_source_get_all(context, marker=None, limit=None, sort_keys=None, _process_controller_info_filters, _controller_get), models.Port: (_port_get_query, _process_port_info_filters, _port_get), + models.Disk: (_disk_get_query, _process_disk_info_filters, + _disk_get), } diff --git a/delfin/db/sqlalchemy/models.py b/delfin/db/sqlalchemy/models.py index a0d02b58e..8dd77d07d 100644 --- a/delfin/db/sqlalchemy/models.py +++ b/delfin/db/sqlalchemy/models.py @@ -119,14 +119,21 @@ class Disk(BASE, DelfinBase): """Represents a disk object.""" __tablename__ = 'disks' id = Column(String(36), primary_key=True) - name = Column(String(255)) - status = Column(String(255)) - vendor = Column(String(255)) native_disk_id = Column(String(255)) + name = Column(String(255)) serial_number = Column(String(255)) + manufacturer = Column(String(255)) model = Column(String(255)) - media_type = Column(String(255)) + firmware = Column(String(255)) + speed = Column(Integer) capacity = Column(BigInteger) + status = Column(String(255)) + physical_type = Column(String(255)) + logical_type = Column(String(255)) + health_score = Column(Integer) + native_disk_group_id = Column(String(255)) + storage_id = Column(String(255)) + location = Column(String(255)) class Controller(BASE, DelfinBase): diff --git a/delfin/drivers/api.py b/delfin/drivers/api.py index a2e6f0ccf..5d80b642c 100644 --- a/delfin/drivers/api.py +++ b/delfin/drivers/api.py @@ -99,6 +99,11 @@ def list_ports(self, context, storage_id): driver = self.driver_manager.get_driver(context, storage_id=storage_id) return driver.list_ports(context) + def list_disks(self, context, storage_id): + """List all disks from storage system.""" + driver = self.driver_manager.get_driver(context, storage_id=storage_id) + return driver.list_disks(context) + def add_trap_config(self, context, storage_id, trap_config): """Config the trap receiver in storage system.""" pass diff --git a/delfin/drivers/dell_emc/vmax/vmax.py b/delfin/drivers/dell_emc/vmax/vmax.py index af4490381..d9eb0db1a 100644 --- a/delfin/drivers/dell_emc/vmax/vmax.py +++ b/delfin/drivers/dell_emc/vmax/vmax.py @@ -84,6 +84,9 @@ def list_controllers(self, context): def list_ports(self, context): pass + def list_disks(self, context): + pass + def add_trap_config(self, context, trap_config): pass diff --git a/delfin/drivers/driver.py b/delfin/drivers/driver.py index 74bd1c550..ca280ddc7 100644 --- a/delfin/drivers/driver.py +++ b/delfin/drivers/driver.py @@ -58,6 +58,11 @@ def list_ports(self, context): """List all ports from storage system.""" pass + @abc.abstractmethod + def list_disks(self, context): + """List all disks from storage system.""" + pass + @abc.abstractmethod def add_trap_config(self, context, trap_config): """Config the trap receiver in storage system.""" diff --git a/delfin/drivers/fake_storage/__init__.py b/delfin/drivers/fake_storage/__init__.py index 90011e951..46835f6f5 100644 --- a/delfin/drivers/fake_storage/__init__.py +++ b/delfin/drivers/fake_storage/__init__.py @@ -50,6 +50,7 @@ MIN_WAIT, MAX_WAIT = 0.1, 0.5 MIN_POOL, MAX_POOL = 1, 100 MIN_PORTS, MAX_PORTS = 1, 10 +MIN_DISK, MAX_DISK = 1, 100 MIN_VOLUME, MAX_VOLUME = 1, 2000 MIN_CONTROLLERS, MAX_CONTROLLERS = 1, 5 PAGE_LIMIT = 500 @@ -231,6 +232,41 @@ def list_ports(self, ctx): port_list.append(c) return port_list + def list_disks(self, ctx): + rd_disks_count = random.randint(MIN_DISK, MAX_DISK) + LOG.info("###########fake_disks for %s: %d" % (self.storage_id, + rd_disks_count)) + disk_list = [] + for idx in range(rd_disks_count): + max_s, normal, remain = self._get_random_capacity() + manufacturer = ["Intel", "Seagate", "WD", "Crucial", "HP"] + sts = list(constants.DiskStatus.ALL) + sts_len = len(constants.DiskStatus.ALL) - 1 + physical_type = list(constants.DiskPhysicalType.ALL) + physical_type_len = len(constants.DiskPhysicalType.ALL) - 1 + logic_type = list(constants.DiskLogicalType.ALL) + logic_type_len = len(constants.DiskLogicalType.ALL) - 1 + c = { + "name": "fake_disk_" + str(idx), + "storage_id": self.storage_id, + "native_disk_id": "fake_original_id_" + str(idx), + "serial_number": "serial_" + str(random.randint(0, 9999)), + "manufacturer": manufacturer[random.randint(0, 4)], + "model": "model_" + str(random.randint(0, 9999)), + "firmware": "firmware_" + str(random.randint(0, 9999)), + "speed": normal, + "capacity": max_s, + "status": sts[random.randint(0, sts_len)], + "physical_type": physical_type[ + random.randint(0, physical_type_len)], + "logical_type": logic_type[random.randint(0, logic_type_len)], + "health_score": random.randint(0, 100), + "native_diskgroup_id": "dg_id_" + str(random.randint(0, 99)), + "location": "location_" + str(random.randint(0, 99)), + } + disk_list.append(c) + return disk_list + def add_trap_config(self, context, trap_config): pass diff --git a/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py b/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py index 1cb1881d1..b69977625 100644 --- a/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py +++ b/delfin/drivers/hpe/hpe_3par/hpe_3parstor.py @@ -72,6 +72,9 @@ def list_controllers(self, context): def list_ports(self, context): pass + def list_disks(self, context): + pass + def list_alerts(self, context, query_para=None): return self.alert_handler.list_alerts(context, query_para) diff --git a/delfin/drivers/huawei/oceanstor/oceanstor.py b/delfin/drivers/huawei/oceanstor/oceanstor.py index ef62c1700..9e6e5592b 100644 --- a/delfin/drivers/huawei/oceanstor/oceanstor.py +++ b/delfin/drivers/huawei/oceanstor/oceanstor.py @@ -184,6 +184,9 @@ def list_controllers(self, context): def list_ports(self, context): pass + def list_disks(self, context): + pass + def add_trap_config(self, context, trap_config): pass diff --git a/delfin/exception.py b/delfin/exception.py index 882d5749e..32231564b 100644 --- a/delfin/exception.py +++ b/delfin/exception.py @@ -178,6 +178,10 @@ class PortNotFound(NotFound): msg_fmt = _("Port {0} could not be found.") +class DiskNotFound(NotFound): + msg_fmt = _("Disk {0} could not be found.") + + class StorageDriverNotFound(NotFound): msg_fmt = _("Storage driver '{0}'could not be found.") diff --git a/delfin/task_manager/tasks/resources.py b/delfin/task_manager/tasks/resources.py index 41344339e..d144e44e7 100644 --- a/delfin/task_manager/tasks/resources.py +++ b/delfin/task_manager/tasks/resources.py @@ -347,6 +347,56 @@ def remove(self): db.port_delete_by_storage(self.context, self.storage_id) +class StorageDiskTask(StorageResourceTask): + def __init__(self, context, storage_id): + super(StorageDiskTask, self).__init__(context, storage_id) + + @check_deleted() + @set_synced_after() + def sync(self): + """ + :return: + """ + LOG.info('Syncing disks for storage id:{0}'.format(self.storage_id)) + try: + # collect the disks list from driver and database + storage_disks = self.driver_api.list_disks(self.context, + self.storage_id) + db_disks = db.disk_get_all(self.context, + filters={"storage_id": + self.storage_id}) + + add_list, update_list, delete_id_list = self._classify_resources( + storage_disks, db_disks, 'native_disk_id' + ) + + LOG.info('###StorageDiskTask for {0}:add={1},delete={2},' + 'update={3}'.format(self.storage_id, + len(add_list), + len(delete_id_list), + len(update_list))) + if delete_id_list: + db.disks_delete(self.context, delete_id_list) + + if update_list: + db.disks_update(self.context, update_list) + + if add_list: + db.disks_create(self.context, add_list) + except AttributeError as e: + LOG.error(e) + except Exception as e: + msg = _('Failed to sync disks entry in DB: {0}' + .format(e)) + LOG.error(msg) + else: + LOG.info("Syncing disks successful!!!") + + def remove(self): + LOG.info('Remove disks for storage id:{0}'.format(self.storage_id)) + db.disk_delete_by_storage(self.context, self.storage_id) + + class PerformanceCollectionTask(object): def __init__(self): diff --git a/delfin/tests/unit/db/test_db_api.py b/delfin/tests/unit/db/test_db_api.py index 616434c81..319f9be99 100644 --- a/delfin/tests/unit/db/test_db_api.py +++ b/delfin/tests/unit/db/test_db_api.py @@ -337,6 +337,68 @@ def test_port_get_all(self, mock_session): result = db_api.port_get_all(ctxt, filters={'status': 'Normal'}) assert len(result) == 0 + @mock.patch('delfin.db.sqlalchemy.api.get_session') + def test_disk_get(self, mock_session): + fake_disk = {} + mock_session.return_value.__enter__.return_value.query.return_value \ + = fake_disk + result = db_api.disk_get(ctxt, + 'c5c91c98-91aa-40e6-85ac-37a1d3b32bd') + assert len(result) == 0 + + @mock.patch('delfin.db.sqlalchemy.api.get_session') + def test_disks_update(self, mock_session): + disks = [{'id': 'c5c91c98-91aa-40e6-85ac-37a1d3b32bd'}] + mock_session.return_value.__enter__.return_value.query.return_value \ + = disks + result = db_api.disks_update(ctxt, disks) + assert len(result) == 1 + + @mock.patch('delfin.db.sqlalchemy.api.get_session') + def test_disk_update(self, mock_session): + disks = [{'id': 'c5c91c98-91aa-40e6-85ac-37a1d3b32bd'}] + mock_session.return_value.__enter__.return_value.query.return_value \ + = disks + result = db_api.disk_update(ctxt, + 'c5c91c98-91aa-40e6-85ac-37a1d3b32bd', + disks) + assert len(result) == 0 + + @mock.patch('delfin.db.sqlalchemy.api.get_session') + def test_disks_delete(self, mock_session): + fake_disk = ['c5c91c98-91aa-40e6-85ac-37a1d3b32bd'] + mock_session.return_value.__enter__.return_value.query.return_value \ + = fake_disk + result = db_api.disks_delete(ctxt, fake_disk) + assert result is None + + @mock.patch('delfin.db.sqlalchemy.api.get_session') + def test_disks_create(self, mock_session): + fake_disk = [models.Volume()] + mock_session.return_value.__enter__.return_value.query.return_value \ + = fake_disk + result = db_api.disks_create(ctxt, fake_disk) + assert len(result) == 1 + + @mock.patch('delfin.db.sqlalchemy.api.get_session') + def test_disk_create(self, mock_session): + fake_disk = models.Volume() + mock_session.return_value.__enter__.return_value.query.return_value \ + = fake_disk + result = db_api.disk_create(ctxt, fake_disk) + assert len(result) == 0 + + @mock.patch('delfin.db.sqlalchemy.api.get_session') + def test_disk_get_all(self, mock_session): + fake_disk = [] + mock_session.return_value.__enter__.return_value.query.return_value \ + = fake_disk + result = db_api.disk_get_all(ctxt) + assert len(result) == 0 + + result = db_api.disk_get_all(ctxt, filters={'status': 'Normal'}) + assert len(result) == 0 + @mock.patch('delfin.db.sqlalchemy.api.get_session') def test_access_info_get_all(self, mock_session): fake_access_info = [] diff --git a/delfin/tests/unit/drivers/test_api.py b/delfin/tests/unit/drivers/test_api.py index d1225064d..9c73ac6e8 100644 --- a/delfin/tests/unit/drivers/test_api.py +++ b/delfin/tests/unit/drivers/test_api.py @@ -316,10 +316,31 @@ def test_list_ports(self, mock_storage, mock_access_info, storage_id = '12345' driver = api.driver_manager.driver_factory.get(storage_id, None) self.assertIsNotNone(driver) - api.list_ports(context, storage_id) mock_fake.assert_called_once() + @mock.patch.object(FakeStorageDriver, 'list_disks') + @mock.patch('delfin.db.storage_create') + @mock.patch('delfin.db.access_info_create') + @mock.patch('delfin.db.storage_get_all') + def test_list_disks(self, mock_storage, mock_access_info, + mock_storage_create, mock_fake): + storage = copy.deepcopy(STORAGE) + storage['id'] = '12345' + mock_storage.return_value = None + mock_access_info.return_value = ACCESS_INFO + mock_storage_create.return_value = storage + mock_fake.return_value = [] + api = API() + api.discover_storage(context, ACCESS_INFO) + + storage_id = '12345' + driver = api.driver_manager.driver_factory.get(storage_id, None) + self.assertIsNotNone(driver) + + api.list_disks(context, storage_id) + mock_fake.assert_called_once() + @mock.patch.object(FakeStorageDriver, 'parse_alert') @mock.patch('delfin.db.storage_create') @mock.patch('delfin.db.access_info_create') diff --git a/delfin/tests/unit/task_manager/test_resources.py b/delfin/tests/unit/task_manager/test_resources.py index 2849c32ca..96c020129 100644 --- a/delfin/tests/unit/task_manager/test_resources.py +++ b/delfin/tests/unit/task_manager/test_resources.py @@ -85,7 +85,6 @@ } ] - controllers_list = [{ 'id': '12c2d52f-01bc-41f5-b73f-7abf6f38a222', "name": "fake_controller_" + str(id), @@ -99,6 +98,25 @@ } ] +disks_list = [{ + 'id': '12c2d52f-01bc-41f5-b73f-7abf6f38a2a6', + "name": "fake_pool_" + str(id), + "storage_id": '12c2d52f-01bc-41f5-b73f-7abf6f38a2a6', + "native_disk_id": "fake_original_id_" + str(id), + "serial_number": "serial_3299", + "manufacturer": "Intel", + "model": "model_4565", + "firmware": "firmware_9541", + "speed": 751, + "capacity": 1074, + "status": "offline", + "physical_type": "sata", + "logical_type": "cache", + "health_score": 34, + "native_disk_group_id": "", +} +] + class TestStorageDeviceTask(test.TestCase): def setUp(self): @@ -335,3 +353,49 @@ def test_remove(self, mock_port_del): context, 'c5c91c98-91aa-40e6-85ac-37a1d3b32bda') port_obj.remove() self.assertTrue(mock_port_del.called) + + +class TestStorageDiskTask(test.TestCase): + @mock.patch.object(coordination.LOCK_COORDINATOR, 'get_lock') + @mock.patch('delfin.drivers.api.API.list_disks') + @mock.patch('delfin.db.disk_get_all') + @mock.patch('delfin.db.disks_delete') + @mock.patch('delfin.db.disks_update') + @mock.patch('delfin.db.disks_create') + def test_sync_successful(self, mock_disk_create, mock_disk_update, + mock_disk_del, mock_disk_get_all, mock_list_disks, + get_lock): + disk_obj = resources.StorageDiskTask( + context, 'c5c91c98-91aa-40e6-85ac-37a1d3b32bda') + disk_obj.sync() + self.assertTrue(mock_list_disks.called) + self.assertTrue(mock_disk_get_all.called) + self.assertTrue(get_lock.called) + + # collect the disks from fake_storage + fake_storage_obj = fake_storage.FakeStorageDriver() + + # add the disks to DB + mock_list_disks.return_value = fake_storage_obj.list_disks(context) + mock_disk_get_all.return_value = list() + disk_obj.sync() + self.assertTrue(mock_disk_create.called) + + # update the disks to DB + mock_list_disks.return_value = disks_list + mock_disk_get_all.return_value = disks_list + disk_obj.sync() + self.assertTrue(mock_disk_update.called) + + # delete the disks to DB + mock_list_disks.return_value = list() + mock_disk_get_all.return_value = disks_list + disk_obj.sync() + self.assertTrue(mock_disk_del.called) + + @mock.patch('delfin.db.disk_delete_by_storage') + def test_remove(self, mock_disk_del): + disk_obj = resources.StorageDiskTask( + context, 'c5c91c98-91aa-40e6-85ac-37a1d3b32bda') + disk_obj.remove() + self.assertTrue(mock_disk_del.called)