]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Case sensitivity problem in cinder scheduler
authorYusuke Hayashi <hayashi-yusuke@jp.fujitsu.com>
Tue, 13 Oct 2015 23:53:49 +0000 (08:53 +0900)
committerYusuke Hayashi <hayashi-yusuke@jp.fujitsu.com>
Fri, 23 Oct 2015 10:10:05 +0000 (19:10 +0900)
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

cinder/db/sqlalchemy/api.py
cinder/tests/unit/test_db_api.py

index 9e540ea14fd2d27027316864a62ee96e2e7ad6d9..cd841f5b24bd088665e5c2a933d8e2729dd65859 100644 (file)
@@ -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
index 7e66f733ab0a56dd270fd112b7830cda0f67fb76..97ae9ca7d64b298577bfadecc3596c71ed2e79df 100644 (file)
@@ -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):