diff options
| author | Rick Harris <rconradharris@gmail.com> | 2012-03-08 02:55:04 +0000 |
|---|---|---|
| committer | Rick Harris <rconradharris@gmail.com> | 2012-03-08 03:34:36 +0000 |
| commit | 08b4e6c2b808011ea7ae9b367bfb829cb332f4e7 (patch) | |
| tree | d299d19dd3d5ae7cd400ec2ba371cd45394fd193 /nova/db | |
| parent | 70f0ea588e9b0ddf47c15531b86e81aa59556199 (diff) | |
| download | nova-08b4e6c2b808011ea7ae9b367bfb829cb332f4e7.tar.gz nova-08b4e6c2b808011ea7ae9b367bfb829cb332f4e7.tar.xz nova-08b4e6c2b808011ea7ae9b367bfb829cb332f4e7.zip | |
Fix racey snapshots.
Fixes bug 949475
Atomically tests and sets the instance task_state before allowing a
snapshot or backup to be initiated.
Change-Id: I40671a80f5e75337e176a715837f62d400cc21b6
Diffstat (limited to 'nova/db')
| -rw-r--r-- | nova/db/api.py | 9 | ||||
| -rw-r--r-- | nova/db/sqlalchemy/api.py | 34 |
2 files changed, 43 insertions, 0 deletions
diff --git a/nova/db/api.py b/nova/db/api.py index bf94747df..9d4fae11e 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -647,6 +647,15 @@ def instance_get_all_hung_in_rebooting(context, reboot_window): return IMPL.instance_get_all_hung_in_rebooting(context, reboot_window) +def instance_test_and_set(context, instance_id, attr, ok_states, + new_state): + """Atomically check if an instance is in a valid state, and if it is, set + the instance into a new state. + """ + return IMPL.instance_test_and_set( + context, instance_id, attr, ok_states, new_state) + + def instance_update(context, instance_id, values): """Set the given properties on an instance and update it. diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 07d48a229..ff9ffd0bc 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1691,6 +1691,40 @@ def instance_get_all_hung_in_rebooting(context, reboot_window, session=None): @require_context +def instance_test_and_set(context, instance_id, attr, ok_states, + new_state, session=None): + """Atomically check if an instance is in a valid state, and if it is, set + the instance into a new state. + """ + if not session: + session = get_session() + + with session.begin(): + query = model_query(context, models.Instance, session=session, + project_only=True) + + if utils.is_uuid_like(instance_id): + query = query.filter_by(uuid=instance_id) + else: + query = query.filter_by(id=instance_id) + + # NOTE(vish): if with_lockmode isn't supported, as in sqlite, + # then this has concurrency issues + instance = query.with_lockmode('update').first() + + state = instance[attr] + if state not in ok_states: + raise exception.InstanceInvalidState( + attr=attr, + instance_uuid=instance['uuid'], + state=state, + method='instance_test_and_set') + + instance[attr] = new_state + instance.save(session=session) + + +@require_context def instance_update(context, instance_id, values): session = get_session() |
