summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nova/db/sqlalchemy/api.py12
-rw-r--r--nova/db/sqlalchemy/migrate_repo/versions/399_add_instances_hidden.py6
-rw-r--r--nova/objects/instance.py4
-rw-r--r--nova/tests/unit/objects/test_instance.py50
-rw-r--r--releasenotes/notes/instances_hidden_after_upgrade_to_train-9ce4731f31bc6bd2.yaml19
5 files changed, 85 insertions, 6 deletions
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py
index 7a00a0a4c8..02311f240d 100644
--- a/nova/db/sqlalchemy/api.py
+++ b/nova/db/sqlalchemy/api.py
@@ -2257,9 +2257,15 @@ def instance_get_all_by_filters_sort(context, filters, limit=None, marker=None,
else:
filters['user_id'] = context.user_id
- if 'hidden' not in filters:
- # Filter out hidden instances by default.
- filters['hidden'] = False
+ if filters.pop('hidden', False):
+ query_prefix = query_prefix.filter(models.Instance.hidden == true())
+ else:
+ # If the query should not include hidden instances, then
+ # filter instances with hidden=False or hidden=NULL because
+ # older records may have no value set.
+ query_prefix = query_prefix.filter(or_(
+ models.Instance.hidden == false(),
+ models.Instance.hidden == null()))
# Filters for exact matches that we can do along with the SQL query...
# For other filters that don't match this, we will do regexp matching
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/399_add_instances_hidden.py b/nova/db/sqlalchemy/migrate_repo/versions/399_add_instances_hidden.py
index 1e96d06886..aa382bab3c 100644
--- a/nova/db/sqlalchemy/migrate_repo/versions/399_add_instances_hidden.py
+++ b/nova/db/sqlalchemy/migrate_repo/versions/399_add_instances_hidden.py
@@ -20,5 +20,9 @@ def upgrade(migrate_engine):
for prefix in ('', 'shadow_'):
instances = Table('%sinstances' % prefix, meta, autoload=True)
if not hasattr(instances.c, 'hidden'):
- hidden = Column('hidden', Boolean, default=False)
+ # NOTE(danms): This column originally included default=False. We
+ # discovered in bug #1862205 that this will attempt to rewrite
+ # the entire instances table with that value, which can time out
+ # for large data sets (and does not even abort).
+ hidden = Column('hidden', Boolean)
instances.create_column(hidden)
diff --git a/nova/objects/instance.py b/nova/objects/instance.py
index 467a129045..87c2eea602 100644
--- a/nova/objects/instance.py
+++ b/nova/objects/instance.py
@@ -1504,7 +1504,9 @@ class InstanceList(base.ObjectListBase, base.NovaObject):
# NOTE(mriedem): Filter out hidden instances since there should be a
# non-hidden version of the instance in another cell database and the
# API will only show one of them, so we don't count the hidden copy.
- project_query = project_query.filter_by(hidden=false())
+ project_query = project_query.filter(
+ or_(models.Instance.hidden == false(),
+ models.Instance.hidden == null()))
project_result = project_query.first()
fields = ('instances', 'cores', 'ram')
diff --git a/nova/tests/unit/objects/test_instance.py b/nova/tests/unit/objects/test_instance.py
index 36c90372d0..c108deb1db 100644
--- a/nova/tests/unit/objects/test_instance.py
+++ b/nova/tests/unit/objects/test_instance.py
@@ -27,6 +27,8 @@ from oslo_versionedobjects import base as ovo_base
from nova.compute import task_states
from nova.compute import vm_states
from nova.db import api as db
+from nova.db.sqlalchemy import api as sql_api
+from nova.db.sqlalchemy import models as sql_models
from nova import exception
from nova.network import model as network_model
from nova import notifications
@@ -1981,7 +1983,53 @@ class _TestInstanceListObject(object):
class TestInstanceListObject(test_objects._LocalTest,
_TestInstanceListObject):
- pass
+ # No point in doing this db-specific test twice for remote
+ def test_hidden_filter_query(self):
+ """Check that our instance_get_by_filters() honors hidden properly
+
+ As reported in bug #1862205, we need to properly handle instances
+ with the hidden field set to NULL and not expect SQLAlchemy to
+ translate those values on SELECT.
+ """
+ values = {'user_id': self.context.user_id,
+ 'project_id': self.context.project_id,
+ 'host': 'foo'}
+
+ for hidden_value in (True, False):
+ db.instance_create(self.context,
+ dict(values, hidden=hidden_value))
+
+ # NOTE(danms): Because the model has default=False, we can not use
+ # it to create an instance with a hidden value of NULL. So, do it
+ # manually here.
+ engine = sql_api.get_engine()
+ table = sql_models.Instance.__table__
+ with engine.connect() as conn:
+ update = table.insert().values(user_id=self.context.user_id,
+ project_id=self.context.project_id,
+ uuid=uuids.nullinst,
+ host='foo',
+ hidden=None)
+ conn.execute(update)
+
+ insts = objects.InstanceList.get_by_filters(self.context,
+ {'hidden': True})
+ # We created one hidden instance above, so expect only that one
+ # to come out of this query.
+ self.assertEqual(1, len(insts))
+
+ # We created one unhidden instance above, and one specifically
+ # with a NULL value to represent an unmigrated instance, which
+ # defaults to hidden=False, so expect both of those here.
+ insts = objects.InstanceList.get_by_filters(self.context,
+ {'hidden': False})
+ self.assertEqual(2, len(insts))
+
+ # Do the same check as above, but make sure hidden=False is the
+ # default behavior.
+ insts = objects.InstanceList.get_by_filters(self.context,
+ {})
+ self.assertEqual(2, len(insts))
class TestRemoteInstanceListObject(test_objects._RemoteTest,
diff --git a/releasenotes/notes/instances_hidden_after_upgrade_to_train-9ce4731f31bc6bd2.yaml b/releasenotes/notes/instances_hidden_after_upgrade_to_train-9ce4731f31bc6bd2.yaml
new file mode 100644
index 0000000000..4bbbb15694
--- /dev/null
+++ b/releasenotes/notes/instances_hidden_after_upgrade_to_train-9ce4731f31bc6bd2.yaml
@@ -0,0 +1,19 @@
+---
+upgrade:
+ - |
+ Upgrading to Train on a deployment with a large database may hit
+ `bug 1862205`_, which results in instance records left in a bad
+ state, and manifests as instances not being shown in list
+ operations. Users upgrading to Train for the first time will
+ definitely want to apply a version which includes this fix. Users
+ already on Train should upgrade to a version including this fix to
+ ensure the problem is addressed.
+
+ .. _bug 1862205: https://launchpad.net/bugs/1862205
+fixes:
+ - |
+ A fix for serious `bug 1862205`_ is provided which addresses both
+ the performance aspect of schema migration 399, as well as the
+ potential fallout for cases where this migration silently fails
+ and leaves large numbers of instances hidden from view from the
+ API.