Browse Source

Change ensure share to make startup faster

This driver interface is currently being used wrong and needs a rewrite.
This patch adds the ability to solve the potential problem of slow start
up, and deal with non-user-initiated state changes to shares.

Implement BP ensure-share
Change-Id: I704591446cd5f6ad2293c5a18eccf5412d488baf
changes/45/457545/48
zhongjun 5 years ago
committed by zhongjun
parent
commit
e351f3b98f
  1. 12
      manila/db/api.py
  2. 62
      manila/db/migrations/alembic/versions/4a482571410f_add_backends_info_table.py
  3. 53
      manila/db/sqlalchemy/api.py
  4. 7
      manila/db/sqlalchemy/models.py
  5. 47
      manila/share/driver.py
  6. 15
      manila/share/drivers/lvm.py
  7. 106
      manila/share/manager.py
  8. 5
      manila/share/utils.py
  9. 25
      manila/tests/db/migrations/alembic/migrations_data_checks.py
  10. 58
      manila/tests/db/sqlalchemy/test_api.py
  11. 8
      manila/tests/share/drivers/test_lvm.py
  12. 180
      manila/tests/share/test_manager.py
  13. 4
      releasenotes/notes/enhance-ensure-share-58fc14ffc099f481.yaml

12
manila/db/api.py

@ -1323,3 +1323,15 @@ def message_destroy(context, message_id):
def cleanup_expired_messages(context):
"""Soft delete expired messages"""
return IMPL.cleanup_expired_messages(context)
def backend_info_get(context, host):
"""Get hash info for given host."""
return IMPL.backend_info_get(context, host)
def backend_info_update(context, host, value=None,
delete_existing=False):
"""Update hash info for host."""
return IMPL.backend_info_update(context, host=host, value=value,
delete_existing=delete_existing)

62
manila/db/migrations/alembic/versions/4a482571410f_add_backends_info_table.py

@ -0,0 +1,62 @@
# Copyright 2017 Huawei inc.
# All Rights Reserved.
#
# 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.
"""add_backend_info_table
Revision ID: 4a482571410f
Revises: 27cb96d991fa
Create Date: 2017-05-18 14:47:38.201658
"""
# revision identifiers, used by Alembic.
revision = '4a482571410f'
down_revision = '27cb96d991fa'
from alembic import op
from oslo_log import log
import sqlalchemy as sql
LOG = log.getLogger(__name__)
backend_info_table_name = 'backend_info'
def upgrade():
try:
op.create_table(
backend_info_table_name,
sql.Column('created_at', sql.DateTime),
sql.Column('updated_at', sql.DateTime),
sql.Column('deleted_at', sql.DateTime),
sql.Column('deleted', sql.Integer, default=0),
sql.Column('host', sql.String(255),
nullable=False, primary_key=True),
sql.Column('info_hash', sql.String(255),
nullable=False),
mysql_engine='InnoDB',
)
except Exception:
LOG.error("Table |%s| not created!",
backend_info_table_name)
raise
def downgrade():
try:
op.drop_table(backend_info_table_name)
except Exception:
LOG.error("%s table not dropped", backend_info_table_name)
raise

53
manila/db/sqlalchemy/api.py

@ -4884,3 +4884,56 @@ def cleanup_expired_messages(context):
with session.begin():
return session.query(models.Message).filter(
models.Message.expires_at < now).delete()
@require_context
def backend_info_get(context, host):
"""Get hash info for given host."""
session = get_session()
result = _backend_info_query(session, context, host)
return result
@require_context
def backend_info_create(context, host, value):
session = get_session()
with session.begin():
info_ref = models.BackendInfo()
info_ref.update({"host": host,
"info_hash": value})
info_ref.save(session)
return info_ref
@require_context
def backend_info_update(context, host, value=None, delete_existing=False):
"""Remove backend info for host name."""
session = get_session()
with session.begin():
info_ref = _backend_info_query(session, context, host)
if info_ref:
if value:
info_ref.update({"info_hash": value})
elif delete_existing and info_ref['deleted'] != 1:
info_ref.update({"deleted": 1,
"deleted_at": timeutils.utcnow()})
else:
info_ref = models.BackendInfo()
info_ref.update({"host": host,
"info_hash": value})
info_ref.save(session)
return info_ref
def _backend_info_query(session, context, host, read_deleted=False):
result = model_query(
context, models.BackendInfo, session=session,
read_deleted=read_deleted,
).filter_by(
host=host,
).first()
return result

7
manila/db/sqlalchemy/models.py

@ -1220,6 +1220,13 @@ class Message(BASE, ManilaBase):
deleted = Column(String(36), default='False')
class BackendInfo(BASE, ManilaBase):
"""Represent Backend Info."""
__tablename__ = "backend_info"
host = Column(String(255), primary_key=True)
info_hash = Column(String(255))
def register_models():
"""Register Models and create metadata.

47
manila/share/driver.py

@ -2504,3 +2504,50 @@ class ShareDriver(object):
"and 'ipv6_support' are both False.",
data['share_backend_name'])
return data
def get_backend_info(self, context):
"""Get driver and array configuration parameters.
Driver can use this method to get the special configuration info and
return for assessment.
:returns: A dictionary containing driver-specific info.
Example::
{
'version': '2.23'
'port': '80',
'logicalportip': '1.1.1.1',
...
}
"""
raise NotImplementedError()
def ensure_shares(self, context, shares):
"""Invoked to ensure that shares are exported.
Driver can use this method to update the list of export locations of
the shares if it changes. To do that, a dictionary of shares should
be returned.
:shares: None or a list of all shares for updates.
:returns: None or a dictionary of updates in the format.
Example::
{
'09960614-8574-4e03-89cf-7cf267b0bd08': {
'export_locations': [{...}, {...}],
'status': 'error',
},
'28f6eabb-4342-486a-a7f4-45688f0c0295': {
'export_locations': [{...}, {...}],
'status': 'available',
},
}
"""
raise NotImplementedError()

15
manila/share/drivers/lvm.py

@ -281,11 +281,18 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
# remove dir
self._execute('rmdir', mount_path, run_as_root=True)
def ensure_shares(self, context, shares):
updates = {}
for share in shares:
updates[share['id']] = {
'export_locations': self.ensure_share(context, share)}
return updates
def ensure_share(self, ctx, share, share_server=None):
"""Ensure that storage are mounted and exported."""
device_name = self._get_local_path(share)
self._mount_device(share, device_name)
self._get_helper(share).create_exports(
return self._get_helper(share).create_exports(
self.share_server, share['name'], recreate=True)
def _delete_share(self, ctx, share):
@ -515,3 +522,9 @@ class LVMShareDriver(LVMMixin, driver.ShareDriver):
share['id'])
return updated_shares
def get_backend_info(self, context):
return {
'export_ips': ','.join(self.share_server['public_addresses']),
'db_version': utils.get_recent_db_migration_id(),
}

106
manila/share/manager.py

@ -22,6 +22,7 @@
import copy
import datetime
import functools
import hashlib
from oslo_config import cfg
from oslo_log import log
@ -312,9 +313,49 @@ class ShareManager(manager.SchedulerDependentManager):
_driver_setup()
share_instances = self.db.share_instances_get_all_by_host(ctxt,
self.host)
self.ensure_driver_resources(ctxt)
self.publish_service_capabilities(ctxt)
LOG.info("Finished initialization of driver: '%(driver)s"
"@%(host)s'",
{"driver": self.driver.__class__.__name__,
"host": self.host})
def ensure_driver_resources(self, ctxt):
old_backend_info_hash = self.db.backend_info_get(ctxt, self.host)
new_backend_info = None
new_backend_info_hash = None
update_share_instances = []
try:
new_backend_info = self.driver.get_backend_info(ctxt)
except Exception as e:
if not isinstance(e, NotImplementedError):
LOG.exception(
("The backend %(host)s could not get backend info."),
{'host': self.host})
raise
else:
LOG.debug(
("The backend %(host)s does not support get backend"
" info method."),
{'host': self.host})
if new_backend_info:
new_backend_info_hash = hashlib.sha1(six.text_type(
sorted(new_backend_info.items())).encode('utf-8')).hexdigest()
if (old_backend_info_hash and
old_backend_info_hash == new_backend_info_hash):
LOG.debug(
("The ensure share be skipped because the The old backend "
"%(host)s info as the same as new backend info"),
{'host': self.host})
return
share_instances = self.db.share_instances_get_all_by_host(
ctxt, self.host)
LOG.debug("Re-exporting %s shares", len(share_instances))
for share_instance in share_instances:
share_ref = self.db.share_get(ctxt, share_instance['share_id'])
@ -337,21 +378,44 @@ class ShareManager(manager.SchedulerDependentManager):
continue
self._ensure_share_instance_has_pool(ctxt, share_instance)
share_server = self._get_share_server(ctxt, share_instance)
share_instance = self.db.share_instance_get(
ctxt, share_instance['id'], with_share_data=True)
try:
export_locations = self.driver.ensure_share(
ctxt, share_instance, share_server=share_server)
except Exception:
update_share_instances.append(share_instance)
try:
update_share_instances = self.driver.ensure_shares(
ctxt, update_share_instances)
except Exception as e:
if not isinstance(e, NotImplementedError):
LOG.exception("Caught exception trying ensure "
"share '%(s_id)s'.",
{'s_id': share_instance['id']})
"share instances.")
else:
self._ensure_share(ctxt, update_share_instances)
if new_backend_info:
self.db.backend_info_update(
ctxt, self.host, new_backend_info_hash)
for share_instance in share_instances:
if share_instance['id'] not in update_share_instances:
continue
if update_share_instances[share_instance['id']].get('status'):
self.db.share_instance_update(
ctxt, share_instance['id'],
{'status': (
update_share_instances[share_instance['id']].
get('status')),
'host': share_instance['host']}
)
if export_locations:
update_export_location = (
update_share_instances[share_instance['id']]
.get('export_locations'))
if update_export_location:
self.db.share_export_locations_update(
ctxt, share_instance['id'], export_locations)
ctxt, share_instance['id'], update_export_location)
share_server = self._get_share_server(ctxt, share_instance)
if share_instance['access_rules_status'] != (
constants.STATUS_ACTIVE):
@ -396,11 +460,21 @@ class ShareManager(manager.SchedulerDependentManager):
"access rules for snapshot instance %s.",
snap_instance['id'])
self.publish_service_capabilities(ctxt)
LOG.info("Finished initialization of driver: '%(driver)s"
"@%(host)s'",
{"driver": self.driver.__class__.__name__,
"host": self.host})
def _ensure_share(self, ctxt, share_instances):
for share_instance in share_instances:
try:
share_server = self._get_share_server(
ctxt, share_instance)
export_locations = self.driver.ensure_share(
ctxt, share_instance, share_server=share_server)
except Exception:
LOG.exception("Caught exception trying ensure "
"share '%(s_id)s'.",
{'s_id': share_instance['id']})
continue
if export_locations:
self.db.share_export_locations_update(
ctxt, share_instance['id'], export_locations)
def _provide_share_server_for_share(self, context, share_network_id,
share_instance, snapshot=None,

5
manila/share/utils.py

@ -19,6 +19,7 @@
from oslo_config import cfg
from manila.common import constants
from manila.db import migration
from manila import rpc
from manila import utils
@ -147,3 +148,7 @@ def _usage_from_share(share_ref, share_instance_ref, **extra_usage_info):
usage_info.update(extra_usage_info)
return usage_info
def get_recent_db_migration_id():
return migration.version()

25
manila/tests/db/migrations/alembic/migrations_data_checks.py

@ -2537,3 +2537,28 @@ class NewDescriptionColumnChecks(BaseMigrationChecks):
db_result = engine.execute(table.select())
for record in db_result:
self.test_case.assertFalse(hasattr(record, 'description'))
@map_to_migration('4a482571410f')
class BackenInfoTableChecks(BaseMigrationChecks):
new_table_name = 'backend_info'
def setup_upgrade_data(self, engine):
pass
def check_upgrade(self, engine, data):
data = {
'host': 'test_host',
'info_hash': 'test_hash',
'created_at': datetime.datetime(2017, 7, 10, 18, 5, 58),
'updated_at': None,
'deleted_at': None,
'deleted': 0,
}
new_table = utils.load_table(self.new_table_name, engine)
engine.execute(new_table.insert(data))
def check_downgrade(self, engine):
self.test_case.assertRaises(sa_exc.NoSuchTableError, utils.load_table,
self.new_table_name, engine)

58
manila/tests/db/sqlalchemy/test_api.py

@ -2861,3 +2861,61 @@ class MessagesDatabaseAPITestCase(test.TestCase):
db_api.cleanup_expired_messages(adm_context)
messages = db_api.message_get_all(adm_context)
self.assertEqual(2, len(messages))
class BackendInfoDatabaseAPITestCase(test.TestCase):
def setUp(self):
"""Run before each test."""
super(BackendInfoDatabaseAPITestCase, self).setUp()
self.ctxt = context.get_admin_context()
def test_create(self):
host = "fake_host"
value = "fake_hash_value"
initial_data = db_api.backend_info_get(self.ctxt, host)
db_api.backend_info_update(self.ctxt, host, value)
actual_data = db_api.backend_info_get(self.ctxt, host)
self.assertIsNone(initial_data)
self.assertEqual(value, actual_data['info_hash'])
self.assertEqual(host, actual_data['host'])
def test_get(self):
host = "fake_host"
value = "fake_hash_value"
db_api.backend_info_update(self.ctxt, host, value, False)
actual_result = db_api.backend_info_get(self.ctxt, host)
self.assertEqual(value, actual_result['info_hash'])
self.assertEqual(host, actual_result['host'])
def test_delete(self):
host = "fake_host"
value = "fake_hash_value"
db_api.backend_info_update(self.ctxt, host, value)
initial_data = db_api.backend_info_get(self.ctxt, host)
db_api.backend_info_update(self.ctxt, host, delete_existing=True)
actual_data = db_api.backend_info_get(self.ctxt, host)
self.assertEqual(value, initial_data['info_hash'])
self.assertEqual(host, initial_data['host'])
self.assertIsNone(actual_data)
def test_double_update(self):
host = "fake_host"
value_1 = "fake_hash_value_1"
value_2 = "fake_hash_value_2"
initial_data = db_api.backend_info_get(self.ctxt, host)
db_api.backend_info_update(self.ctxt, host, value_1)
db_api.backend_info_update(self.ctxt, host, value_2)
actual_data = db_api.backend_info_get(self.ctxt, host)
self.assertIsNone(initial_data)
self.assertEqual(value_2, actual_data['info_hash'])
self.assertEqual(host, actual_data['host'])

8
manila/tests/share/drivers/test_lvm.py

@ -681,3 +681,11 @@ class LVMShareDriverTestCase(test.TestCase):
self._driver.update_share_usage_size,
self._context,
[self.share])
def test_get_backend_info(self):
backend_info = self._driver.get_backend_info(self._context)
self.assertEqual(
{'export_ips': ','.join(self.server['public_addresses']),
'db_version': mock.ANY},
backend_info)

180
manila/tests/share/test_manager.py

@ -15,6 +15,7 @@
"""Test of Share Manager for Manila."""
import datetime
import hashlib
import random
import ddt
@ -293,7 +294,12 @@ class ShareManagerTestCase(test.TestCase):
return instances, rules
def test_init_host_with_shares_and_rules(self):
@ddt.data(("588569466613133740", {"db_version": "test_version"}),
(None, {"db_version": "test_version"}),
(None, None))
@ddt.unpack
def test_init_host_with_shares_and_rules(self, old_backend_info_hash,
new_backend_info):
# initialization of test data
def raise_share_access_exists(*args, **kwargs):
@ -302,7 +308,20 @@ class ShareManagerTestCase(test.TestCase):
instances, rules = self._setup_init_mocks()
fake_export_locations = ['fake/path/1', 'fake/path']
fake_update_instances = {
instances[0]['id']: {'export_locations': fake_export_locations},
instances[2]['id']: {'export_locations': fake_export_locations}
}
instances[0]['access_rules_status'] = ''
instances[2]['access_rules_status'] = ''
share_server = 'fake_share_server_type_does_not_matter'
self.mock_object(self.share_manager.db,
'backend_info_get',
mock.Mock(return_value=old_backend_info_hash))
mock_backend_info_update = self.mock_object(
self.share_manager.db, 'backend_info_update')
self.mock_object(self.share_manager.driver, 'get_backend_info',
mock.Mock(return_value=new_backend_info))
self.mock_object(self.share_manager.db,
'share_instances_get_all_by_host',
mock.Mock(return_value=instances))
@ -311,8 +330,8 @@ class ShareManagerTestCase(test.TestCase):
instances[4]]))
self.mock_object(self.share_manager.db,
'share_export_locations_update')
self.mock_object(self.share_manager.driver, 'ensure_share',
mock.Mock(return_value=fake_export_locations))
self.mock_object(self.share_manager.driver, 'ensure_shares',
mock.Mock(return_value=fake_update_instances))
self.mock_object(self.share_manager, '_ensure_share_instance_has_pool')
self.mock_object(self.share_manager, '_get_share_server',
mock.Mock(return_value=share_server))
@ -343,30 +362,64 @@ class ShareManagerTestCase(test.TestCase):
utils.IsAMatcher(context.RequestContext))
(self.share_manager.driver.check_for_setup_error.
assert_called_once_with())
if new_backend_info:
self.share_manager.db.backend_info_update.assert_called_once_with(
utils.IsAMatcher(context.RequestContext),
self.share_manager.host, hashlib.sha1(six.text_type(sorted(
new_backend_info.items())).encode('utf-8')).hexdigest())
else:
mock_backend_info_update.assert_not_called()
self.share_manager._ensure_share_instance_has_pool.assert_has_calls([
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
])
self.share_manager._get_share_server.assert_has_calls([
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
])
self.share_manager.driver.ensure_share.assert_has_calls([
mock.call(utils.IsAMatcher(context.RequestContext), instances[0],
share_server=share_server),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2],
share_server=share_server),
mock.call(utils.IsAMatcher(context.RequestContext),
instances[0]),
mock.call(utils.IsAMatcher(context.RequestContext),
instances[2]),
])
self.share_manager.driver.ensure_shares.assert_called_once_with(
utils.IsAMatcher(context.RequestContext),
[instances[0], instances[2], instances[4]])
(self.share_manager.publish_service_capabilities.
assert_called_once_with(
utils.IsAMatcher(context.RequestContext)))
self.share_manager.access_helper.update_access_rules.assert_has_calls([
mock.call(mock.ANY, instances[4]['id'], share_server=share_server),
mock.call(mock.ANY, instances[0]['id'], share_server=share_server),
mock.call(mock.ANY, instances[2]['id'], share_server=share_server),
])
def test_init_host_with_exception_on_ensure_share(self):
def raise_exception(*args, **kwargs):
raise exception.ManilaException(message="Fake raise")
def test_init_host_without_shares_and_rules(self):
new_backend_info = {"db_version": "sdfesxcv"}
self.mock_object(
self.share_manager.db, 'backend_info_get',
mock.Mock(return_value=hashlib.sha1(six.text_type(sorted(
new_backend_info.items())).encode('utf-8')).hexdigest()))
self.mock_object(self.share_manager.driver, 'get_backend_info',
mock.Mock(return_value=new_backend_info))
self.mock_object(self.share_manager, 'publish_service_capabilities',
mock.Mock())
mock_ensure_shares = self.mock_object(
self.share_manager.driver, 'ensure_shares')
mock_share_instances_get_all_by_host = self.mock_object(
self.share_manager.db, 'share_instances_get_all_by_host')
# call of 'init_host' method
self.share_manager.init_host()
self.share_manager.driver.do_setup.assert_called_once_with(
utils.IsAMatcher(context.RequestContext))
self.share_manager.db.backend_info_get.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), self.share_manager.host)
self.share_manager.driver.get_backend_info.assert_called_once_with(
utils.IsAMatcher(context.RequestContext))
mock_ensure_shares.assert_not_called()
mock_share_instances_get_all_by_host.assert_not_called()
@ddt.data(exception.ManilaException, ['fake/path/1', 'fake/path'])
def test_init_host_with_ensure_share(self, expected_ensure_share_result):
def raise_NotImplementedError(*args, **kwargs):
raise NotImplementedError
instances = self._setup_init_mocks(setup_access_rules=False)
share_server = 'fake_share_server_type_does_not_matter'
@ -376,9 +429,13 @@ class ShareManagerTestCase(test.TestCase):
self.mock_object(self.share_manager.db, 'share_instance_get',
mock.Mock(side_effect=[instances[0], instances[2],
instances[3]]))
self.mock_object(
self.share_manager.driver, 'ensure_shares',
mock.Mock(side_effect=raise_NotImplementedError))
self.mock_object(self.share_manager.driver, 'ensure_share',
mock.Mock(side_effect=raise_exception))
self.mock_object(self.share_manager, '_ensure_share_instance_has_pool')
mock.Mock(side_effect=expected_ensure_share_result))
self.mock_object(
self.share_manager, '_ensure_share_instance_has_pool')
self.mock_object(self.share_manager, '_get_share_server',
mock.Mock(return_value=share_server))
self.mock_object(self.share_manager, 'publish_service_capabilities')
@ -399,6 +456,9 @@ class ShareManagerTestCase(test.TestCase):
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
])
self.share_manager.driver.ensure_shares.assert_called_once_with(
utils.IsAMatcher(context.RequestContext),
[instances[0], instances[2], instances[3]])
self.share_manager._get_share_server.assert_has_calls([
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
@ -422,12 +482,83 @@ class ShareManagerTestCase(test.TestCase):
{'id': instances[1]['id'], 'status': instances[1]['status']},
)
def test_init_host_with_exception_on_ensure_shares(self):
def raise_exception(*args, **kwargs):
raise exception.ManilaException(message="Fake raise")
instances = self._setup_init_mocks(setup_access_rules=False)
mock_ensure_share = self.mock_object(
self.share_manager.driver, 'ensure_share')
self.mock_object(self.share_manager.db,
'share_instances_get_all_by_host',
mock.Mock(return_value=instances))
self.mock_object(self.share_manager.db, 'share_instance_get',
mock.Mock(side_effect=[instances[0], instances[2],
instances[3]]))
self.mock_object(
self.share_manager.driver, 'ensure_shares',
mock.Mock(side_effect=raise_exception))
self.mock_object(
self.share_manager, '_ensure_share_instance_has_pool')
# call of 'init_host' method
self.share_manager.init_host()
# verification of call
(self.share_manager.db.share_instances_get_all_by_host.
assert_called_once_with(utils.IsAMatcher(context.RequestContext),
self.share_manager.host))
self.share_manager.driver.do_setup.assert_called_once_with(
utils.IsAMatcher(context.RequestContext))
self.share_manager.driver.check_for_setup_error.assert_called_with()
self.share_manager._ensure_share_instance_has_pool.assert_has_calls([
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
])
self.share_manager.driver.ensure_shares.assert_called_once_with(
utils.IsAMatcher(context.RequestContext),
[instances[0], instances[2], instances[3]])
mock_ensure_share.assert_not_called()
def test_init_host_with_exception_on_get_backend_info(self):
def raise_exception(*args, **kwargs):
raise exception.ManilaException(message="Fake raise")
mock_ensure_share = self.mock_object(
self.share_manager.driver, 'ensure_share')
mock_ensure_shares = self.mock_object(
self.share_manager.driver, 'ensure_shares')
self.mock_object(self.share_manager.db,
'backend_info_get',
mock.Mock(return_value="test_backend_info"))
self.mock_object(
self.share_manager.driver, 'get_backend_info',
mock.Mock(side_effect=raise_exception))
# call of 'init_host' method
self.assertRaises(
exception.ManilaException,
self.share_manager.init_host,
)
# verification of call
self.share_manager.db.backend_info_get.assert_called_once_with(
utils.IsAMatcher(context.RequestContext), self.share_manager.host)
self.share_manager.driver.get_backend_info.assert_called_once_with(
utils.IsAMatcher(context.RequestContext))
mock_ensure_share.assert_not_called()
mock_ensure_shares.assert_not_called()
def test_init_host_with_exception_on_update_access_rules(self):
def raise_exception(*args, **kwargs):
raise exception.ManilaException(message="Fake raise")
instances, rules = self._setup_init_mocks()
share_server = 'fake_share_server_type_does_not_matter'
fake_update_instances = {
instances[0]['id']: {'status': 'available'},
instances[2]['id']: {'status': 'available'},
instances[4]['id']: {'status': 'available'}
}
smanager = self.share_manager
self.mock_object(smanager.db, 'share_instances_get_all_by_host',
mock.Mock(return_value=instances))
@ -436,6 +567,8 @@ class ShareManagerTestCase(test.TestCase):
instances[4]]))
self.mock_object(self.share_manager.driver, 'ensure_share',
mock.Mock(return_value=None))
self.mock_object(self.share_manager.driver, 'ensure_shares',
mock.Mock(return_value=fake_update_instances))
self.mock_object(smanager, '_ensure_share_instance_has_pool')
self.mock_object(smanager, '_get_share_server',
mock.Mock(return_value=share_server))
@ -461,16 +594,9 @@ class ShareManagerTestCase(test.TestCase):
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
])
smanager._get_share_server.assert_has_calls([
mock.call(utils.IsAMatcher(context.RequestContext), instances[0]),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2]),
])
smanager.driver.ensure_share.assert_has_calls([
mock.call(utils.IsAMatcher(context.RequestContext), instances[0],
share_server=share_server),
mock.call(utils.IsAMatcher(context.RequestContext), instances[2],
share_server=share_server),
])
smanager.driver.ensure_shares.assert_called_once_with(
utils.IsAMatcher(context.RequestContext),
[instances[0], instances[2], instances[4]])
(self.share_manager.publish_service_capabilities.
assert_called_once_with(
utils.IsAMatcher(context.RequestContext)))

4
releasenotes/notes/enhance-ensure-share-58fc14ffc099f481.yaml

@ -0,0 +1,4 @@
---
fixes:
- Adds the ability to solve the potential problem of slow start
up, and deal with non-user-initiated state changes to shares.
Loading…
Cancel
Save