Wait for volume creation
If users create a container with new cinder volumes and the volumes are 'creating'. Zun should wait for the volume creation before proceeding the container creation process. Change-Id: I423a69a97271a2acdd73a85cccddb65b052d169e Closes-Bug: #1740942
This commit is contained in:
parent
d53aa2d2dc
commit
ffc8b684b6
@ -13,6 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
import time
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_service import periodic_task
|
from oslo_service import periodic_task
|
||||||
@ -129,10 +130,26 @@ class Manager(periodic_task.PeriodicTasks):
|
|||||||
container.host = None
|
container.host = None
|
||||||
container.save(context)
|
container.save(context)
|
||||||
|
|
||||||
|
def _wait_for_volumes_available(self, context, volumes, timeout=60,
|
||||||
|
poll_interval=1):
|
||||||
|
count = 0
|
||||||
|
start_time = time.time()
|
||||||
|
while time.time() - start_time < timeout:
|
||||||
|
if count == len(volumes):
|
||||||
|
break
|
||||||
|
for vol in volumes:
|
||||||
|
if self.driver.is_volume_available(context, vol):
|
||||||
|
count = count + 1
|
||||||
|
time.sleep(poll_interval)
|
||||||
|
else:
|
||||||
|
self.fail("Volumes did not reach available status after %d s"
|
||||||
|
% (timeout))
|
||||||
|
|
||||||
def container_create(self, context, limits, requested_networks,
|
def container_create(self, context, limits, requested_networks,
|
||||||
requested_volumes, container, run, pci_requests=None):
|
requested_volumes, container, run, pci_requests=None):
|
||||||
@utils.synchronized(container.uuid)
|
@utils.synchronized(container.uuid)
|
||||||
def do_container_create():
|
def do_container_create():
|
||||||
|
self._wait_for_volumes_available(context, requested_volumes)
|
||||||
if not self._attach_volumes(context, container, requested_volumes):
|
if not self._attach_volumes(context, container, requested_volumes):
|
||||||
return
|
return
|
||||||
created_container = self._do_container_create(
|
created_container = self._do_container_create(
|
||||||
|
@ -798,6 +798,12 @@ class DockerDriver(driver.ContainerDriver):
|
|||||||
context=context)
|
context=context)
|
||||||
volume_driver.delete(volume_mapping)
|
volume_driver.delete(volume_mapping)
|
||||||
|
|
||||||
|
def is_volume_available(self, context, volume_mapping):
|
||||||
|
volume_driver = vol_driver.driver(
|
||||||
|
provider=volume_mapping.volume_provider,
|
||||||
|
context=context)
|
||||||
|
return volume_driver.is_volume_available(volume_mapping)
|
||||||
|
|
||||||
def _get_or_create_docker_network(self, context, network_api,
|
def _get_or_create_docker_network(self, context, network_api,
|
||||||
neutron_net_id):
|
neutron_net_id):
|
||||||
docker_net_name = self._get_docker_network_name(context,
|
docker_net_name = self._get_docker_network_name(context,
|
||||||
|
@ -204,6 +204,9 @@ class ContainerDriver(object):
|
|||||||
def delete_volume(self, context, volume_mapping):
|
def delete_volume(self, context, volume_mapping):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def is_volume_available(self, context, volume_mapping):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
def add_security_group(self, context, container, security_group, **kwargs):
|
def add_security_group(self, context, container, security_group, **kwargs):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -256,10 +256,12 @@ class TestManager(base.TestCase):
|
|||||||
@mock.patch('zun.image.driver.pull_image')
|
@mock.patch('zun.image.driver.pull_image')
|
||||||
@mock.patch.object(fake_driver, 'detach_volume')
|
@mock.patch.object(fake_driver, 'detach_volume')
|
||||||
@mock.patch.object(fake_driver, 'attach_volume')
|
@mock.patch.object(fake_driver, 'attach_volume')
|
||||||
|
@mock.patch.object(fake_driver, 'is_volume_available')
|
||||||
@mock.patch.object(fake_driver, 'create')
|
@mock.patch.object(fake_driver, 'create')
|
||||||
@mock.patch.object(fake_driver, 'start')
|
@mock.patch.object(fake_driver, 'start')
|
||||||
def test_container_run(
|
def test_container_run(
|
||||||
self, mock_start, mock_create, mock_attach_volume,
|
self, mock_start, mock_create,
|
||||||
|
mock_is_volume_available, mock_attach_volume,
|
||||||
mock_detach_volume, mock_pull, mock_list_by_container, mock_save,
|
mock_detach_volume, mock_pull, mock_list_by_container, mock_save,
|
||||||
mock_spawn_n):
|
mock_spawn_n):
|
||||||
container = Container(self.context, **utils.get_test_container())
|
container = Container(self.context, **utils.get_test_container())
|
||||||
@ -285,6 +287,7 @@ class TestManager(base.TestCase):
|
|||||||
mock_start.assert_called_once_with(self.context, container)
|
mock_start.assert_called_once_with(self.context, container)
|
||||||
mock_attach_volume.assert_called_once()
|
mock_attach_volume.assert_called_once()
|
||||||
mock_detach_volume.assert_not_called()
|
mock_detach_volume.assert_not_called()
|
||||||
|
mock_is_volume_available.assert_called_once()
|
||||||
self.assertEqual(1, len(FakeVolumeMapping.volumes))
|
self.assertEqual(1, len(FakeVolumeMapping.volumes))
|
||||||
|
|
||||||
@mock.patch('zun.common.utils.spawn_n')
|
@mock.patch('zun.common.utils.spawn_n')
|
||||||
@ -294,12 +297,15 @@ class TestManager(base.TestCase):
|
|||||||
@mock.patch('zun.image.driver.pull_image')
|
@mock.patch('zun.image.driver.pull_image')
|
||||||
@mock.patch.object(fake_driver, 'detach_volume')
|
@mock.patch.object(fake_driver, 'detach_volume')
|
||||||
@mock.patch.object(fake_driver, 'attach_volume')
|
@mock.patch.object(fake_driver, 'attach_volume')
|
||||||
|
@mock.patch.object(fake_driver, 'is_volume_available')
|
||||||
@mock.patch.object(fake_driver, 'create')
|
@mock.patch.object(fake_driver, 'create')
|
||||||
@mock.patch.object(fake_driver, 'start')
|
@mock.patch.object(fake_driver, 'start')
|
||||||
def test_container_run_driver_attach_failed(
|
def test_container_run_driver_attach_failed(
|
||||||
self, mock_start, mock_create, mock_attach_volume,
|
self, mock_start, mock_create,
|
||||||
|
mock_is_volume_available, mock_attach_volume,
|
||||||
mock_detach_volume, mock_pull, mock_list_by_container, mock_save,
|
mock_detach_volume, mock_pull, mock_list_by_container, mock_save,
|
||||||
mock_spawn_n):
|
mock_spawn_n):
|
||||||
|
mock_is_volume_available.return_value = True
|
||||||
mock_attach_volume.side_effect = [None, base.TestingException("fake")]
|
mock_attach_volume.side_effect = [None, base.TestingException("fake")]
|
||||||
container = Container(self.context, **utils.get_test_container())
|
container = Container(self.context, **utils.get_test_container())
|
||||||
vol = FakeVolumeMapping()
|
vol = FakeVolumeMapping()
|
||||||
@ -334,9 +340,11 @@ class TestManager(base.TestCase):
|
|||||||
side_effect=FakeVolumeMapping.list_by_container)
|
side_effect=FakeVolumeMapping.list_by_container)
|
||||||
@mock.patch.object(fake_driver, 'detach_volume')
|
@mock.patch.object(fake_driver, 'detach_volume')
|
||||||
@mock.patch.object(fake_driver, 'attach_volume')
|
@mock.patch.object(fake_driver, 'attach_volume')
|
||||||
|
@mock.patch.object(fake_driver, 'is_volume_available')
|
||||||
@mock.patch('zun.image.driver.pull_image')
|
@mock.patch('zun.image.driver.pull_image')
|
||||||
def test_container_run_image_not_found(
|
def test_container_run_image_not_found(
|
||||||
self, mock_pull, mock_attach_volume, mock_detach_volume,
|
self, mock_pull, mock_is_volume_available,
|
||||||
|
mock_attach_volume, mock_detach_volume,
|
||||||
mock_list_by_container, mock_save, mock_spawn_n):
|
mock_list_by_container, mock_save, mock_spawn_n):
|
||||||
container_dict = utils.get_test_container(
|
container_dict = utils.get_test_container(
|
||||||
image='test:latest', image_driver='docker',
|
image='test:latest', image_driver='docker',
|
||||||
@ -360,6 +368,7 @@ class TestManager(base.TestCase):
|
|||||||
'ifnotpresent', 'docker')
|
'ifnotpresent', 'docker')
|
||||||
mock_attach_volume.assert_called_once()
|
mock_attach_volume.assert_called_once()
|
||||||
mock_detach_volume.assert_called_once()
|
mock_detach_volume.assert_called_once()
|
||||||
|
mock_is_volume_available.assert_called_once()
|
||||||
self.assertEqual(0, len(FakeVolumeMapping.volumes))
|
self.assertEqual(0, len(FakeVolumeMapping.volumes))
|
||||||
|
|
||||||
@mock.patch('zun.common.utils.spawn_n')
|
@mock.patch('zun.common.utils.spawn_n')
|
||||||
@ -368,9 +377,11 @@ class TestManager(base.TestCase):
|
|||||||
side_effect=FakeVolumeMapping.list_by_container)
|
side_effect=FakeVolumeMapping.list_by_container)
|
||||||
@mock.patch.object(fake_driver, 'detach_volume')
|
@mock.patch.object(fake_driver, 'detach_volume')
|
||||||
@mock.patch.object(fake_driver, 'attach_volume')
|
@mock.patch.object(fake_driver, 'attach_volume')
|
||||||
|
@mock.patch.object(fake_driver, 'is_volume_available')
|
||||||
@mock.patch('zun.image.driver.pull_image')
|
@mock.patch('zun.image.driver.pull_image')
|
||||||
def test_container_run_image_pull_exception_raised(
|
def test_container_run_image_pull_exception_raised(
|
||||||
self, mock_pull, mock_attach_volume, mock_detach_volume,
|
self, mock_pull, mock_is_volume_available,
|
||||||
|
mock_attach_volume, mock_detach_volume,
|
||||||
mock_list_by_container, mock_save, mock_spawn_n):
|
mock_list_by_container, mock_save, mock_spawn_n):
|
||||||
container_dict = utils.get_test_container(
|
container_dict = utils.get_test_container(
|
||||||
image='test:latest', image_driver='docker',
|
image='test:latest', image_driver='docker',
|
||||||
@ -394,6 +405,7 @@ class TestManager(base.TestCase):
|
|||||||
'ifnotpresent', 'docker')
|
'ifnotpresent', 'docker')
|
||||||
mock_attach_volume.assert_called_once()
|
mock_attach_volume.assert_called_once()
|
||||||
mock_detach_volume.assert_called_once()
|
mock_detach_volume.assert_called_once()
|
||||||
|
mock_is_volume_available.assert_called_once()
|
||||||
self.assertEqual(0, len(FakeVolumeMapping.volumes))
|
self.assertEqual(0, len(FakeVolumeMapping.volumes))
|
||||||
|
|
||||||
@mock.patch('zun.common.utils.spawn_n')
|
@mock.patch('zun.common.utils.spawn_n')
|
||||||
@ -402,9 +414,11 @@ class TestManager(base.TestCase):
|
|||||||
side_effect=FakeVolumeMapping.list_by_container)
|
side_effect=FakeVolumeMapping.list_by_container)
|
||||||
@mock.patch.object(fake_driver, 'detach_volume')
|
@mock.patch.object(fake_driver, 'detach_volume')
|
||||||
@mock.patch.object(fake_driver, 'attach_volume')
|
@mock.patch.object(fake_driver, 'attach_volume')
|
||||||
|
@mock.patch.object(fake_driver, 'is_volume_available')
|
||||||
@mock.patch('zun.image.driver.pull_image')
|
@mock.patch('zun.image.driver.pull_image')
|
||||||
def test_container_run_image_pull_docker_error(
|
def test_container_run_image_pull_docker_error(
|
||||||
self, mock_pull, mock_attach_volume, mock_detach_volume,
|
self, mock_pull, mock_is_volume_available,
|
||||||
|
mock_attach_volume, mock_detach_volume,
|
||||||
mock_list_by_container, mock_save, mock_spawn_n):
|
mock_list_by_container, mock_save, mock_spawn_n):
|
||||||
container_dict = utils.get_test_container(
|
container_dict = utils.get_test_container(
|
||||||
image='test:latest', image_driver='docker',
|
image='test:latest', image_driver='docker',
|
||||||
@ -428,6 +442,7 @@ class TestManager(base.TestCase):
|
|||||||
'ifnotpresent', 'docker')
|
'ifnotpresent', 'docker')
|
||||||
mock_attach_volume.assert_called_once()
|
mock_attach_volume.assert_called_once()
|
||||||
mock_detach_volume.assert_called_once()
|
mock_detach_volume.assert_called_once()
|
||||||
|
mock_is_volume_available.assert_called_once()
|
||||||
self.assertEqual(0, len(FakeVolumeMapping.volumes))
|
self.assertEqual(0, len(FakeVolumeMapping.volumes))
|
||||||
|
|
||||||
@mock.patch('zun.common.utils.spawn_n')
|
@mock.patch('zun.common.utils.spawn_n')
|
||||||
@ -436,12 +451,13 @@ class TestManager(base.TestCase):
|
|||||||
side_effect=FakeVolumeMapping.list_by_container)
|
side_effect=FakeVolumeMapping.list_by_container)
|
||||||
@mock.patch.object(fake_driver, 'detach_volume')
|
@mock.patch.object(fake_driver, 'detach_volume')
|
||||||
@mock.patch.object(fake_driver, 'attach_volume')
|
@mock.patch.object(fake_driver, 'attach_volume')
|
||||||
|
@mock.patch.object(fake_driver, 'is_volume_available')
|
||||||
@mock.patch('zun.image.driver.pull_image')
|
@mock.patch('zun.image.driver.pull_image')
|
||||||
@mock.patch.object(fake_driver, 'create')
|
@mock.patch.object(fake_driver, 'create')
|
||||||
def test_container_run_create_raises_docker_error(
|
def test_container_run_create_raises_docker_error(
|
||||||
self, mock_create, mock_pull, mock_attach_volume,
|
self, mock_create, mock_pull, mock_is_volume_available,
|
||||||
mock_detach_volume, mock_list_by_container, mock_save,
|
mock_attach_volume, mock_detach_volume,
|
||||||
mock_spawn_n):
|
mock_list_by_container, mock_save, mock_spawn_n):
|
||||||
container = Container(self.context, **utils.get_test_container())
|
container = Container(self.context, **utils.get_test_container())
|
||||||
image = {'image': 'repo', 'path': 'out_path', 'driver': 'glance',
|
image = {'image': 'repo', 'path': 'out_path', 'driver': 'glance',
|
||||||
'repo': 'test', 'tag': 'testtag'}
|
'repo': 'test', 'tag': 'testtag'}
|
||||||
@ -467,6 +483,7 @@ class TestManager(base.TestCase):
|
|||||||
self.context, container, image, networks, volumes)
|
self.context, container, image, networks, volumes)
|
||||||
mock_attach_volume.assert_called_once()
|
mock_attach_volume.assert_called_once()
|
||||||
mock_detach_volume.assert_called_once()
|
mock_detach_volume.assert_called_once()
|
||||||
|
mock_is_volume_available.assert_called_once()
|
||||||
self.assertEqual(0, len(FakeVolumeMapping.volumes))
|
self.assertEqual(0, len(FakeVolumeMapping.volumes))
|
||||||
|
|
||||||
@mock.patch.object(FakeResourceTracker,
|
@mock.patch.object(FakeResourceTracker,
|
||||||
|
@ -23,9 +23,9 @@ from zun.common import exception
|
|||||||
from zun.common.i18n import _
|
from zun.common.i18n import _
|
||||||
from zun.common import mount
|
from zun.common import mount
|
||||||
import zun.conf
|
import zun.conf
|
||||||
|
from zun.volume import cinder_api
|
||||||
from zun.volume import cinder_workflow
|
from zun.volume import cinder_workflow
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF = zun.conf.CONF
|
CONF = zun.conf.CONF
|
||||||
@ -72,6 +72,9 @@ class VolumeDriver(object):
|
|||||||
def bind_mount(self, *args, **kwargs):
|
def bind_mount(self, *args, **kwargs):
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def is_volume_available(self, *args, **kwargs):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class Cinder(VolumeDriver):
|
class Cinder(VolumeDriver):
|
||||||
|
|
||||||
@ -116,3 +119,10 @@ class Cinder(VolumeDriver):
|
|||||||
def bind_mount(self, volume):
|
def bind_mount(self, volume):
|
||||||
mountpoint = mount.get_mountpoint(volume.volume_id)
|
mountpoint = mount.get_mountpoint(volume.volume_id)
|
||||||
return mountpoint, volume.container_path
|
return mountpoint, volume.container_path
|
||||||
|
|
||||||
|
def is_volume_available(self, volume):
|
||||||
|
ca = cinder_api.CinderAPI(self.context)
|
||||||
|
if 'available' == ca.get(volume.volume_id).status:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
Loading…
Reference in New Issue
Block a user