From: Yusuke Hayashi Date: Tue, 13 Oct 2015 23:53:49 +0000 (+0900) Subject: Case sensitivity problem in cinder scheduler X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=d75d04ec55f906de5f90d186881bac25688a906b;p=openstack-build%2Fcinder-build.git Case sensitivity problem in cinder scheduler Volume creation fails when we use backend name which consists of same characters as the name used before but different upper/lower case. It happens in MySQL environment. e.g. 1. configure 'lvm' as backend name and start cinder 2. reconfigure 'LVM' as backend name and restart cinder It is caused by the difference of handling of case sensitivity for backend name. When checking the existence of cinder-volume/backend to judge whether new cinder-volume service row in the DB should be created or not, the search-logic is case insensitive for backend name. On the other hand, when searching backend in create_volume processing, the search-logic in cinder-scheduler is case sensitive for backend name. That is why I change the former search-logic from case insensitive one to case sensitive one. Closes-Bug: #1496694 Change-Id: Ibb1565a183961031495b73e985499455a1ea4c7b --- diff --git a/cinder/db/sqlalchemy/api.py b/cinder/db/sqlalchemy/api.py index 9e540ea14..cd841f5b2 100644 --- a/cinder/db/sqlalchemy/api.py +++ b/cinder/db/sqlalchemy/api.py @@ -411,15 +411,16 @@ def _service_get_all_topic_subquery(context, session, topic, subq, label): @require_admin_context def service_get_by_args(context, host, binary): - result = model_query(context, models.Service).\ + results = model_query(context, models.Service).\ filter_by(host=host).\ filter_by(binary=binary).\ - first() + all() - if not result: - raise exception.HostBinaryNotFound(host=host, binary=binary) + for result in results: + if host == result['host']: + return result - return result + raise exception.HostBinaryNotFound(host=host, binary=binary) @require_admin_context diff --git a/cinder/tests/unit/test_db_api.py b/cinder/tests/unit/test_db_api.py index 7e66f733a..97ae9ca7d 100644 --- a/cinder/tests/unit/test_db_api.py +++ b/cinder/tests/unit/test_db_api.py @@ -17,6 +17,7 @@ import datetime import enum +import mock from oslo_config import cfg from oslo_utils import uuidutils import six @@ -226,6 +227,48 @@ class DBAPIServiceTestCase(BaseTest): db.service_get_by_args, self.ctxt, 'non-exists-host', 'a') + @mock.patch('cinder.db.sqlalchemy.api.model_query') + def test_service_get_by_args_with_case_insensitive(self, model_query): + class case_insensitive_filter(object): + def __init__(self, records): + self.records = records + + def filter_by(self, **kwargs): + ret = mock.Mock() + ret.all = mock.Mock() + + results = [] + for record in self.records: + for key, value in kwargs.items(): + if record[key].lower() != value.lower(): + break + else: + results.append(record) + + ret.filter_by = case_insensitive_filter(results).filter_by + ret.all.return_value = results + return ret + + values = [ + {'host': 'host', 'binary': 'a'}, + {'host': 'HOST', 'binary': 'a'} + ] + services = [self._create_service(vals) for vals in values] + + query = mock.Mock() + query.filter_by = case_insensitive_filter(services).filter_by + model_query.return_value = query + + service1 = db.service_get_by_args(self.ctxt, 'host', 'a') + self._assertEqualObjects(services[0], service1) + + service2 = db.service_get_by_args(self.ctxt, 'HOST', 'a') + self._assertEqualObjects(services[1], service2) + + self.assertRaises(exception.HostBinaryNotFound, + db.service_get_by_args, + self.ctxt, 'Host', 'a') + class DBAPIVolumeTestCase(BaseTest):