Commit 30fd1f7f authored by Christos Stavrakakis's avatar Christos Stavrakakis

cyclades: Fix wait-for-sync on stopped instances

Pass 'wait_for_sync' option only on started Ganeti instances, because Ganeti
will fail with 'OpPrereqError' in case the 'wait_for_sync' is set to
False and the instance has deactivated disks.

Note that Cyclades will not let a user neither attach a volume to an instance
that is is in transition state, e.g. stopping, nor change the state of
an instance if there is a pending volume attachment. However, in case
the administrator manually modifies the instance via Ganeti, there is a
race between checking the state of the instance and the time that Ganeti
job will run that cannot be prevented.

Closes grnet/synnefo#314
parent 7063d521
......@@ -1246,9 +1246,15 @@ def attach_volume(vm, volume, depends=[]):
kwargs = {
"instance": vm.backend_vm_id,
"disks": [("add", "-1", disk)],
"wait_for_sync": settings.GANETI_DISKS_WAIT_FOR_SYNC,
"depends": depends,
}
if vm.operstate == "STARTED":
# Pass '--no-wait-for-sync' Ganeti option only if the instance is
# started because Ganeti will complain that this option cannot be
# used for deactivated disks.
kwargs["wait_for_sync"] = settings.GANETI_DISKS_WAIT_FOR_SYNC
if vm.backend.use_hotplug():
kwargs["hotplug_if_possible"] = True
if settings.TEST:
......
......@@ -18,6 +18,7 @@
from django.test import TransactionTestCase
#from snf_django.utils.testing import mocked_quotaholder
from synnefo.logic import servers
from synnefo.logic import backend
from synnefo import quotas
from synnefo.db import models_factory as mfactory, models
from mock import patch, Mock
......@@ -153,6 +154,34 @@ class ServerTest(TransactionTestCase):
self.assertEqual(nics[2]["ip"], None)
self.assertEqual(nics[2]["network"], net.backend_id)
def test_attach_wait_for_sync(self, mrapi):
"""Test wait_for_sync when attaching volume to instance.
"""
volume = mfactory.VolumeFactory()
vm = volume.machine
# Test Started VM
vm.operstate = "STARTED"
vm.save()
mrapi().ModifyInstance.return_value = 1
for sync in [True, False]:
with override_settings(settings, GANETI_DISKS_WAIT_FOR_SYNC=sync):
jobid = backend.attach_volume(vm, volume)
self.assertEqual(jobid, 1)
name, args, kwargs = mrapi().ModifyInstance.mock_calls[-1]
self.assertEqual(kwargs['wait_for_sync'], sync)
# Test Stopped VM. We do not pass wait_for_sync.
vm.operstate = "STOPPED"
vm.save()
mrapi().ModifyInstance.return_value = 1
for sync in [True, False]:
with override_settings(settings, GANETI_DISKS_WAIT_FOR_SYNC=sync):
jobid = backend.attach_volume(vm, volume)
self.assertEqual(jobid, 1)
name, args, kwargs = mrapi().ModifyInstance.mock_calls[-1]
self.assertFalse('wait_for_sync' in kwargs)
@patch("synnefo.logic.rapi_pool.GanetiRapiClient")
class ServerCommandTest(TransactionTestCase):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment