Make the nbd mounter respect CONF.max_nbd_devices.

We need to wait for the config parser to have been run before we
query the value of the max_nbd_devices flag. We will always get
the default value with the previous implementation.

Based on a conversation with Robert Collins. Resolves bug 1088073.

Change-Id: If7b5d6a5c7d8153530e97532aad67c42e744ffec
This commit is contained in:
Michael Still 2012-12-09 10:48:28 +11:00
parent 0e6d3a8826
commit 260dd4f1c7
3 changed files with 121 additions and 1 deletions

View File

@ -0,0 +1,16 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Michael Still
# All Rights Reserved.
#
# 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.

View File

@ -0,0 +1,87 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Michael Still
# All Rights Reserved.
#
# 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 fixtures
import os
from nova import test
from nova.openstack.common import cfg
from nova.virt.disk.mount import nbd
CONF = cfg.CONF
CONF.import_opt('max_nbd_devices', 'nova.virt.disk.mount.nbd')
class NbdTestCase(test.TestCase):
def setUp(self):
super(NbdTestCase, self).setUp()
self.flags(max_nbd_devices=2)
nbd.NbdMount._DEVICES_INITIALIZED = False
nbd.NbdMount._DEVICES = []
def test_nbd_initialize(self):
tempdir = self.useFixture(fixtures.TempDir()).path
n = nbd.NbdMount(None, tempdir)
self.assertTrue(n._DEVICES_INITIALIZED)
self.assertEquals(['/dev/nbd0', '/dev/nbd1'], nbd.NbdMount._DEVICES)
def test_nbd_not_loaded(self):
orig_exists = os.path.exists
tempdir = self.useFixture(fixtures.TempDir()).path
n = nbd.NbdMount(None, tempdir)
# Fake out os.path.exists
def fake_exists(path):
if path.startswith('/sys/block/nbd'):
return False
return orig_exists(path)
self.useFixture(fixtures.MonkeyPatch('os.path.exists', fake_exists))
# This should fail, as we don't have the module "loaded"
# TODO(mikal): work out how to force english as the gettext language
# so that the error check always passes
self.assertEquals(None, n._allocate_nbd())
self.assertEquals('nbd unavailable: module not loaded', n.error)
# And no device should be consumed
self.assertEquals(['/dev/nbd0', '/dev/nbd1'], nbd.NbdMount._DEVICES)
def test_nbd_allocation(self):
orig_exists = os.path.exists
tempdir = self.useFixture(fixtures.TempDir()).path
n = nbd.NbdMount(None, tempdir)
# Fake out os.path.exists
def fake_exists(path):
if path.startswith('/sys/block/nbd'):
if path.endswith('pid'):
return False
return True
return orig_exists(path)
self.useFixture(fixtures.MonkeyPatch('os.path.exists', fake_exists))
# Allocate a nbd device
d = n._allocate_nbd()
self.assertEquals('/dev/nbd1', d)
self.assertEquals(1, len(nbd.NbdMount._DEVICES))
# Allocate another
d = n._allocate_nbd()
self.assertEquals('/dev/nbd0', d)
self.assertEquals([], nbd.NbdMount._DEVICES)

View File

@ -52,17 +52,33 @@ class NbdMount(api.Mount):
# are no free devices. Note that patch currently hardcodes 16 devices.
# We might be able to alleviate problem 2. by scanning /proc/partitions
# like the aformentioned patch does.
_DEVICES = ['/dev/nbd%s' % i for i in range(CONF.max_nbd_devices)]
_DEVICES_INITIALIZED = False
_DEVICES = None
def __init__(self, image, mount_dir, partition=None, device=None):
super(NbdMount, self).__init__(image, mount_dir, partition=partition,
device=device)
# NOTE(mikal): this must be done here, because we need configuration
# to have been parsed before we initialize. Note the scoping to ensure
# we're updating the class scoped variables.
if not self._DEVICES_INITIALIZED:
NbdMount._DEVICES = ['/dev/nbd%s' % i for
i in range(CONF.max_nbd_devices)]
NbdMount._DEVICES_INITIALIZED = True
def _allocate_nbd(self):
if not os.path.exists("/sys/block/nbd0"):
self.error = _('nbd unavailable: module not loaded')
return None
while True:
if not self._DEVICES:
# really want to log this info, not raise
self.error = _('No free nbd devices')
return None
device = self._DEVICES.pop()
if not os.path.exists("/sys/block/%s/pid" %
os.path.basename(device)):
@ -80,6 +96,7 @@ class NbdMount(api.Mount):
device = self._allocate_nbd()
if not device:
return False
LOG.debug(_("Get nbd device %(dev)s for %(imgfile)s") %
{'dev': device, 'imgfile': self.image})
_out, err = utils.trycmd('qemu-nbd', '-c', device, self.image,