summaryrefslogtreecommitdiff
path: root/nova/tests/functional/db/test_request_spec.py
diff options
context:
space:
mode:
authorSylvain Bauza <sbauza@redhat.com>2016-04-07 16:50:02 +0200
committerSylvain Bauza <sbauza@redhat.com>2016-05-17 10:57:48 +0200
commit09f2d4d5ec3a699176d70c2407ced0ce7cd58197 (patch)
treec66c6a379e5bca39c777238d2e52bcf1f4f42dc7 /nova/tests/functional/db/test_request_spec.py
parent52a831e78e3bfbbaa1e129ed931a2d2b0dc48071 (diff)
downloadnova-09f2d4d5ec3a699176d70c2407ced0ce7cd58197.tar.gz
Add a RequestSpec generation migration script
In Mitaka, we began to create and persist a RequestSpec object every time a new instance was requested. Given that instances that were created before that commit do not have a related RequestSpec, we needed to check every time in the conductor methods whether the instance had a request spec associated with it. Now that we are in Newton, we can provide an online data migration script that iterates over all the instances (using a marker), verify if the RequestSpec object is created, and if not, try to persist into that object the legacy fields we already know. The marker uses the request_specs table for persisting itself, and yes this is a hack, but that's the only solution we agreed for making sure we were not looping every time on all the instances (which would be a performance problem). When we finish looping over the instances, we keep that marker so that the next call to that migration script would not again iterate over all the instances (using the limit). Change-Id: I61b9b50436d8bdb8ff06259cc2f876502d688b91 Partially-Implements: blueprint check-destination-on-migrations-newton
Diffstat (limited to 'nova/tests/functional/db/test_request_spec.py')
-rw-r--r--nova/tests/functional/db/test_request_spec.py90
1 files changed, 90 insertions, 0 deletions
diff --git a/nova/tests/functional/db/test_request_spec.py b/nova/tests/functional/db/test_request_spec.py
index 4605709329..c012172bf1 100644
--- a/nova/tests/functional/db/test_request_spec.py
+++ b/nova/tests/functional/db/test_request_spec.py
@@ -10,14 +10,23 @@
# License for the specific language governing permissions and limitations
# under the License.
+from oslo_config import cfg
+
from nova import context
+from nova.db.sqlalchemy import api as db
+from nova.db.sqlalchemy import api_models
from nova import exception
+from nova import objects
from nova.objects import base as obj_base
from nova.objects import request_spec
from nova import test
from nova.tests import fixtures
+from nova.tests.functional import integrated_helpers
+from nova.tests.unit import fake_network
from nova.tests.unit import fake_request_spec
+CONF = cfg.CONF
+
class RequestSpecTestCase(test.NoDBTestCase):
USES_DB_SELF = True
@@ -62,3 +71,84 @@ class RequestSpecTestCase(test.NoDBTestCase):
def test_double_create(self):
spec = self._create_spec()
self.assertRaises(exception.ObjectActionError, spec.create)
+
+
+@db.api_context_manager.writer
+def _delete_request_spec(context, instance_uuid):
+ """Deletes a RequestSpec by the instance_uuid."""
+ context.session.query(api_models.RequestSpec).filter_by(
+ instance_uuid=instance_uuid).delete()
+
+
+class RequestSpecInstanceMigrationTestCase(
+ integrated_helpers._IntegratedTestBase):
+ api_major_version = 'v2.1'
+ _image_ref_parameter = 'imageRef'
+ _flavor_ref_parameter = 'flavorRef'
+
+ def setUp(self):
+ super(RequestSpecInstanceMigrationTestCase, self).setUp()
+
+ self.context = context.get_admin_context()
+ fake_network.set_stub_network_methods(self)
+
+ def _create_instances(self, old=2, total=5):
+ request = self._build_minimal_create_server_request()
+ # Create all instances that would set a RequestSpec object
+ request.update({'max_count': total})
+ self.api.post_server({'server': request})
+
+ self.instances = objects.InstanceList.get_all(self.context)
+ # Make sure that we have all the needed instances
+ self.assertEqual(total, len(self.instances))
+
+ # Fake the legacy behaviour by removing the RequestSpec for some old.
+ for i in range(0, old):
+ _delete_request_spec(self.context, self.instances[i].uuid)
+
+ # Just add a deleted instance to make sure we don't create
+ # a RequestSpec object for it.
+ del request['max_count']
+ server = self.api.post_server({'server': request})
+ self.api.delete_server(server['id'])
+ # Make sure we have the deleted instance only soft-deleted in DB
+ deleted_instances = objects.InstanceList.get_by_filters(
+ self.context, filters={'deleted': True})
+ self.assertEqual(1, len(deleted_instances))
+
+ def test_migration(self):
+ self._create_instances(old=2, total=5)
+
+ match, done = request_spec.migrate_instances_add_request_spec(
+ self.context, 2)
+ self.assertEqual(2, match)
+ self.assertEqual(2, done)
+
+ # Run again the migration call for making sure that we don't check
+ # again the same instances
+ match, done = request_spec.migrate_instances_add_request_spec(
+ self.context, 3)
+ self.assertEqual(3, match)
+ self.assertEqual(0, done)
+
+ # Make sure we ran over all the instances
+ match, done = request_spec.migrate_instances_add_request_spec(
+ self.context, 50)
+ self.assertEqual(0, match)
+ self.assertEqual(0, done)
+
+ # Make sure all instances have now a related RequestSpec
+ for uuid in [instance.uuid for instance in self.instances]:
+ try:
+ objects.RequestSpec.get_by_instance_uuid(self.context, uuid)
+ except exception.RequestSpecNotFound:
+ self.fail("RequestSpec not found for instance UUID :%s ", uuid)
+
+ def test_migration_with_none_old(self):
+ self._create_instances(old=0, total=5)
+
+ # Make sure no migrations can be found
+ match, done = request_spec.migrate_instances_add_request_spec(
+ self.context, 50)
+ self.assertEqual(5, match)
+ self.assertEqual(0, done)