26521718bd
When instances are deployed concurrently on multiple compute nodes, nova-compute may fail on checking direct io capabilities when instance_path is shared across nodes. This fails on shared storage, that doesn't support concurrent write access to the same file, i.e. Virtuozzo Storage. To fix that we add random string at the end of test file name. Closes-Bug: #1727369 Change-Id: I085ade355fba0e9727a38d2dcbc9cffa735a62d1 Signed-off-by: Pavel Glushchak <pglushchak@virtuozzo.com>
128 lines
5.3 KiB
Python
128 lines
5.3 KiB
Python
# Copyright 2011 Justin Santa Barbara
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import errno
|
|
import mock
|
|
import os
|
|
|
|
import nova.privsep.utils
|
|
from nova import test
|
|
|
|
|
|
class SupportDirectIOTestCase(test.NoDBTestCase):
|
|
def setUp(self):
|
|
super(SupportDirectIOTestCase, self).setUp()
|
|
# O_DIRECT is not supported on all Python runtimes, so on platforms
|
|
# where it's not supported (e.g. Mac), we can still test the code-path
|
|
# by stubbing out the value.
|
|
if not hasattr(os, 'O_DIRECT'):
|
|
# `mock` seems to have trouble stubbing an attr that doesn't
|
|
# originally exist, so falling back to stubbing out the attribute
|
|
# directly.
|
|
os.O_DIRECT = 16384
|
|
self.addCleanup(delattr, os, 'O_DIRECT')
|
|
self.einval = OSError()
|
|
self.einval.errno = errno.EINVAL
|
|
self.enoent = OSError()
|
|
self.enoent.errno = errno.ENOENT
|
|
self.test_path = os.path.join('.', '.directio.test.abc123')
|
|
self.io_flags = os.O_CREAT | os.O_WRONLY | os.O_DIRECT
|
|
|
|
open_patcher = mock.patch('os.open')
|
|
write_patcher = mock.patch('os.write')
|
|
close_patcher = mock.patch('os.close')
|
|
unlink_patcher = mock.patch('os.unlink')
|
|
random_string_patcher = mock.patch('nova.utils.generate_random_string',
|
|
return_value='abc123')
|
|
self.addCleanup(open_patcher.stop)
|
|
self.addCleanup(write_patcher.stop)
|
|
self.addCleanup(close_patcher.stop)
|
|
self.addCleanup(unlink_patcher.stop)
|
|
self.addCleanup(random_string_patcher.stop)
|
|
self.mock_open = open_patcher.start()
|
|
self.mock_write = write_patcher.start()
|
|
self.mock_close = close_patcher.start()
|
|
self.mock_unlink = unlink_patcher.start()
|
|
random_string_patcher.start()
|
|
|
|
def test_supports_direct_io(self):
|
|
self.mock_open.return_value = 3
|
|
|
|
self.assertTrue(nova.privsep.utils.supports_direct_io('.'))
|
|
|
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
self.mock_write.assert_called_once_with(3, mock.ANY)
|
|
# ensure unlink(filepath) will actually remove the file by deleting
|
|
# the remaining link to it in close(fd)
|
|
self.mock_close.assert_called_once_with(3)
|
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
|
|
def test_supports_direct_io_with_exception_in_write(self):
|
|
self.mock_open.return_value = 3
|
|
self.mock_write.side_effect = ValueError()
|
|
|
|
self.assertRaises(ValueError, nova.privsep.utils.supports_direct_io,
|
|
'.')
|
|
|
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
self.mock_write.assert_called_once_with(3, mock.ANY)
|
|
# ensure unlink(filepath) will actually remove the file by deleting
|
|
# the remaining link to it in close(fd)
|
|
self.mock_close.assert_called_once_with(3)
|
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
|
|
def test_supports_direct_io_with_exception_in_open(self):
|
|
self.mock_open.side_effect = ValueError()
|
|
|
|
self.assertRaises(ValueError, nova.privsep.utils.supports_direct_io,
|
|
'.')
|
|
|
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
self.mock_write.assert_not_called()
|
|
self.mock_close.assert_not_called()
|
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
|
|
def test_supports_direct_io_with_oserror_in_write(self):
|
|
self.mock_open.return_value = 3
|
|
self.mock_write.side_effect = self.einval
|
|
|
|
self.assertFalse(nova.privsep.utils.supports_direct_io('.'))
|
|
|
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
self.mock_write.assert_called_once_with(3, mock.ANY)
|
|
# ensure unlink(filepath) will actually remove the file by deleting
|
|
# the remaining link to it in close(fd)
|
|
self.mock_close.assert_called_once_with(3)
|
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
|
|
def test_supports_direct_io_with_oserror_in_open_einval(self):
|
|
self.mock_open.side_effect = self.einval
|
|
|
|
self.assertFalse(nova.privsep.utils.supports_direct_io('.'))
|
|
|
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
self.mock_write.assert_not_called()
|
|
self.mock_close.assert_not_called()
|
|
self.mock_unlink.assert_called_once_with(self.test_path)
|
|
|
|
def test_supports_direct_io_with_oserror_in_open_enoent(self):
|
|
self.mock_open.side_effect = self.enoent
|
|
|
|
self.assertFalse(nova.privsep.utils.supports_direct_io('.'))
|
|
|
|
self.mock_open.assert_called_once_with(self.test_path, self.io_flags)
|
|
self.mock_write.assert_not_called()
|
|
self.mock_close.assert_not_called()
|
|
self.mock_unlink.assert_called_once_with(self.test_path)
|