Add bootloader do_action

Partially implements blueprint: pluggable-do-actions

Change-Id: I92c8864dc3021b04d0d0f898198007176a1e92fe
This commit is contained in:
Alexander Gordeev 2016-04-26 21:13:29 +03:00
parent 981a3e9023
commit 478e4c149d
6 changed files with 449 additions and 456 deletions

View File

@ -0,0 +1,182 @@
# Copyright 2016 Mirantis, Inc.
#
# 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.
from io import open
import os
from oslo_config import cfg
from bareon.actions import base
from bareon.drivers.deploy import mixins
from bareon import errors
from bareon.openstack.common import log as logging
from bareon.utils import grub as gu
from bareon.utils import utils
opts = [
cfg.IntOpt(
'grub_timeout',
default=5,
help='Timeout in secs for GRUB'
),
cfg.BoolOpt(
'fix_udev_net_rules',
default=True,
help='Add udev rules for NIC remapping'
),
]
CONF = cfg.CONF
CONF.register_opts(opts)
LOG = logging.getLogger(__name__)
# TODO(agordeev): rename to GrubBootLoaderAction ?
class BootLoaderAction(base.BaseAction, mixins.MountableMixin):
"""BootLoaderAction
installs and configures bootloader
"""
def validate(self):
# TODO(agordeev): implement validate for bootloader
pass
def execute(self):
self.do_bootloader()
def do_bootloader(self):
LOG.debug('--- Installing bootloader (do_bootloader) ---')
chroot = '/tmp/target'
partition_scheme = self.driver.partition_scheme
with self.mount_target(chroot):
mount2uuid = {}
for fs in partition_scheme.fss:
mount2uuid[fs.mount] = utils.execute(
'blkid', '-o', 'value', '-s', 'UUID', fs.device,
check_exit_code=[0])[0].strip()
if '/' not in mount2uuid:
raise errors.WrongPartitionSchemeError(
'Error: device with / mountpoint has not been found')
grub = self.driver.grub
guessed_version = gu.guess_grub_version(chroot=chroot)
if guessed_version != grub.version:
grub.version = guessed_version
LOG.warning('Grub version differs from which the operating '
'system should have by default. Found version in '
'image: %s', guessed_version)
boot_device = partition_scheme.boot_device(grub.version)
install_devices = [d.name for d in partition_scheme.parteds
if d.install_bootloader]
grub.append_kernel_params('root=UUID=%s ' % mount2uuid['/'])
kernel = grub.kernel_name or \
gu.guess_kernel(chroot=chroot, regexp=grub.kernel_regexp)
initrd = grub.initrd_name or \
gu.guess_initrd(chroot=chroot, regexp=grub.initrd_regexp)
if grub.version == 1:
gu.grub1_cfg(kernel=kernel, initrd=initrd,
kernel_params=grub.kernel_params, chroot=chroot,
grub_timeout=CONF.grub_timeout)
gu.grub1_install(install_devices, boot_device, chroot=chroot)
else:
# TODO(kozhukalov): implement which kernel to use by default
# Currently only grub1_cfg accepts kernel and initrd
# parameters.
gu.grub2_cfg(kernel_params=grub.kernel_params, chroot=chroot,
grub_timeout=CONF.grub_timeout)
gu.grub2_install(install_devices, chroot=chroot)
# TODO(agordeev): move to separate actions?
if CONF.fix_udev_net_rules:
# FIXME(agordeev) There's no convenient way to perfrom NIC
# remapping in Ubuntu, so injecting files prior the first boot
# should work
with open(chroot + '/etc/udev/rules.d/70-persistent-net.rules',
'wt', encoding='utf-8') as f:
f.write(u'# Generated by bareon during provisioning: '
u'BEGIN\n')
# pattern is aa:bb:cc:dd:ee:ff_eth0,aa:bb:cc:dd:ee:ff_eth1
for mapping in self.driver.configdrive_scheme. \
common.udevrules.split(','):
mac_addr, nic_name = mapping.split('_')
f.write(u'SUBSYSTEM=="net", ACTION=="add", '
u'DRIVERS=="?*", ATTR{address}=="%s", '
u'ATTR{type}=="1", KERNEL=="eth*", '
u'NAME="%s"\n' % (mac_addr, nic_name))
f.write(
u'# Generated by bareon during provisioning: END\n')
# FIXME(agordeev): Disable net-generator that adds new entries
# to 70-persistent-net.rules
with open(chroot + '/etc/udev/rules.d/'
'75-persistent-net-generator.rules', 'wt',
encoding='utf-8') as f:
f.write(u'# Generated by bareon during provisioning:\n'
u'# DO NOT DELETE. It is needed to disable '
u'net-generator\n')
# FIXME(kozhukalov): Prevent nailgun-agent from doing anything.
# This ugly hack is to be used together with the command removing
# this lock file not earlier than /etc/rc.local
# The reason for this hack to appear is to prevent nailgun-agent
# from changing mcollective config at the same time when cloud-init
# does the same. Otherwise, we can end up with corrupted
# mcollective config.
# For details see https://bugs.launchpad.net/fuel/+bug/1449186
LOG.debug('Preventing nailgun-agent from doing '
'anything until it is unlocked')
utils.makedirs_if_not_exists(os.path.join(chroot,
'etc/nailgun-agent'))
with open(os.path.join(chroot, 'etc/nailgun-agent/nodiscover'),
'w'):
pass
# FIXME(kozhukalov): When we have just os-root fs image and don't
# have os-var-log fs image while / and /var/log are supposed to be
# separate file systems and os-var-log is mounted into
# non-empty directory on the / file system, those files in /var/log
# directory become unavailable.
# The thing is that among those file there is /var/log/upstart
# where upstart daemon writes its logs. We have specific upstart
# job which is to flush open files once all file systems are
# mounted.
# This job needs upstart directory to be available on os-var-log
# file system.
# This is just a temporary fix and correct fix will be available
# soon via updates.
utils.execute('mkdir', '-p', chroot + '/var/log/upstart')
with open(chroot + '/etc/fstab', 'wt', encoding='utf-8') as f:
for fs in self.driver.partition_scheme.fss:
# TODO(kozhukalov): Think of improving the logic so as to
# insert a meaningful fsck order value which is last zero
# at fstab line. Currently we set it into 0 which means
# a corresponding file system will never be checked. We
# assume puppet or other configuration tool will care of
# it.
if fs.mount == '/':
f.write(u'UUID=%s %s %s defaults,errors=panic 0 0\n' %
(mount2uuid[fs.mount], fs.mount, fs.type))
else:
f.write(u'UUID=%s %s %s defaults 0 0\n' %
(mount2uuid[fs.mount], fs.mount, fs.type))

View File

@ -28,7 +28,8 @@ LOG = logging.getLogger(__name__)
class MountableMixin(object):
def _mount_target(self, mount_dir, os_id, pseudo=True, treat_mtab=True):
def _mount_target(self, mount_dir, os_id=None, pseudo=True,
treat_mtab=True):
LOG.debug('Mounting target file systems: %s', mount_dir)
# Here we are going to mount all file systems in partition schema.
for fs in self.driver.partition_scheme.fs_sorted_by_depth(os_id):
@ -53,10 +54,11 @@ class MountableMixin(object):
with open(mtab_path, 'wb') as f:
f.write(mtab)
def _umount_target(self, mount_dir, os_id, pseudo=True):
def _umount_target(self, mount_dir, os_id=None, pseudo=True):
LOG.debug('Umounting target file systems: %s', mount_dir)
if pseudo:
for path in ('/proc', '/dev', '/sys'):
# umount fusectl (typically mounted at /sys/fs/fuse/connections)
for path in ('/proc', '/dev', '/sys/fs/fuse/connections', '/sys'):
fu.umount_fs(os.path.join(mount_dir, path.strip(os.sep)),
try_lazy_umount=True)
for fs in self.driver.partition_scheme.fs_sorted_by_depth(os_id,
@ -66,13 +68,14 @@ class MountableMixin(object):
fu.umount_fs(os.path.join(mount_dir, fs.mount.strip(os.sep)))
@contextmanager
def mount_target(self, mount_dir, os_id, pseudo=True, treat_mtab=True):
self._mount_target(mount_dir, os_id, pseudo=pseudo,
def mount_target(self, mount_dir, os_id=None, pseudo=True,
treat_mtab=True):
self._mount_target(mount_dir, os_id=os_id, pseudo=pseudo,
treat_mtab=treat_mtab)
try:
yield
finally:
self._umount_target(mount_dir, os_id, pseudo)
self._umount_target(mount_dir, os_id=os_id, pseudo=pseudo)
@contextmanager
def _mount_bootloader(self, mount_dir):

View File

@ -21,6 +21,7 @@ from oslo_config import cfg
import six
import yaml
from bareon.actions import bootloader
from bareon.actions import configdrive
from bareon.actions import copyimage
from bareon.actions import partitioning
@ -29,7 +30,6 @@ from bareon import errors
from bareon.openstack.common import log as logging
from bareon.utils import build as bu
from bareon.utils import fs as fu
from bareon.utils import grub as gu
from bareon.utils import utils
opts = [
@ -132,6 +132,9 @@ class Manager(BaseDeployDriver):
def do_copyimage(self):
copyimage.CopyImageAction(self.driver).execute()
def do_bootloader(self):
bootloader.BootLoaderAction(self.driver).execute()
@staticmethod
def _update_metadata_with_repos(metadata, repos):
"""Update action metadata with information about repositories
@ -394,123 +397,6 @@ class Manager(BaseDeployDriver):
with open(meta_file, 'wt') as f:
yaml.safe_dump(drop_data, stream=f, encoding='utf-8')
def do_bootloader(self):
LOG.debug('--- Installing bootloader (do_bootloader) ---')
chroot = '/tmp/target'
self.mount_target(chroot)
mount2uuid = {}
for fs in self.driver.partition_scheme.fss:
mount2uuid[fs.mount] = utils.execute(
'blkid', '-o', 'value', '-s', 'UUID', fs.device,
check_exit_code=[0])[0].strip()
if '/' not in mount2uuid:
raise errors.WrongPartitionSchemeError(
'Error: device with / mountpoint has not been found')
grub = self.driver.grub
guessed_version = gu.guess_grub_version(chroot=chroot)
if guessed_version != grub.version:
grub.version = guessed_version
LOG.warning('Grub version differs from which the operating system '
'should have by default. Found version in image: '
'{0}'.format(guessed_version))
boot_device = self.driver.partition_scheme.boot_device(grub.version)
install_devices = [d.name for d in self.driver.partition_scheme.parteds
if d.install_bootloader]
grub.append_kernel_params('root=UUID=%s ' % mount2uuid['/'])
kernel = grub.kernel_name or gu.guess_kernel(chroot=chroot,
regexp=grub.kernel_regexp)
initrd = grub.initrd_name or gu.guess_initrd(chroot=chroot,
regexp=grub.initrd_regexp)
if grub.version == 1:
gu.grub1_cfg(kernel=kernel, initrd=initrd,
kernel_params=grub.kernel_params, chroot=chroot,
grub_timeout=CONF.grub_timeout)
gu.grub1_install(install_devices, boot_device, chroot=chroot)
else:
# TODO(kozhukalov): implement which kernel to use by default
# Currently only grub1_cfg accepts kernel and initrd parameters.
gu.grub2_cfg(kernel_params=grub.kernel_params, chroot=chroot,
grub_timeout=CONF.grub_timeout)
gu.grub2_install(install_devices, chroot=chroot)
if CONF.fix_udev_net_rules:
# FIXME(agordeev) There's no convenient way to perfrom NIC
# remapping in Ubuntu, so injecting files prior the first boot
# should work
with open(chroot + '/etc/udev/rules.d/70-persistent-net.rules',
'wt', encoding='utf-8') as f:
f.write(u'# Generated by bareon during provisioning: '
u'BEGIN\n')
# pattern is aa:bb:cc:dd:ee:ff_eth0,aa:bb:cc:dd:ee:ff_eth1
for mapping in self.driver.configdrive_scheme. \
common.udevrules.split(','):
mac_addr, nic_name = mapping.split('_')
f.write(u'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
u'ATTR{address}=="%s", ATTR{type}=="1", '
u'KERNEL=="eth*", NAME="%s"\n' % (mac_addr,
nic_name))
f.write(
u'# Generated by bareon during provisioning: END\n')
# FIXME(agordeev): Disable net-generator that will add new etries
# to 70-persistent-net.rules
with open(chroot + '/etc/udev/rules.d/'
'75-persistent-net-generator.rules', 'wt',
encoding='utf-8') as f:
f.write(u'# Generated by bareon during provisioning:\n'
u'# DO NOT DELETE. It is needed to disable '
u'net-generator\n')
# FIXME(kozhukalov): Prevent nailgun-agent from doing anything.
# This ugly hack is to be used together with the command removing
# this lock file not earlier than /etc/rc.local
# The reason for this hack to appear is to prevent nailgun-agent from
# changing mcollective config at the same time when cloud-init
# does the same. Otherwise, we can end up with corrupted mcollective
# config. For details see https://bugs.launchpad.net/fuel/+bug/1449186
LOG.debug('Preventing nailgun-agent from doing '
'anything until it is unlocked')
utils.makedirs_if_not_exists(os.path.join(chroot, 'etc/nailgun-agent'))
with open(os.path.join(chroot, 'etc/nailgun-agent/nodiscover'), 'w'):
pass
# FIXME(kozhukalov): When we have just os-root fs image and don't have
# os-var-log fs image while / and /var/log are supposed to be
# separate file systems and os-var-log is mounted into
# non-empty directory on the / file system, those files in /var/log
# directory become unavailable.
# The thing is that among those file there is /var/log/upstart
# where upstart daemon writes its logs. We have specific upstart job
# which is to flush open files once all file systems are mounted.
# This job needs upstart directory to be available on os-var-log
# file system.
# This is just a temporary fix and correct fix will be available soon
# via updates.
utils.execute('mkdir', '-p', chroot + '/var/log/upstart')
with open(chroot + '/etc/fstab', 'wt', encoding='utf-8') as f:
for fs in self.driver.partition_scheme.fss:
# TODO(kozhukalov): Think of improving the logic so as to
# insert a meaningful fsck order value which is last zero
# at fstab line. Currently we set it into 0 which means
# a corresponding file system will never be checked. We assume
# puppet or other configuration tool will care of it.
if fs.mount == '/':
f.write(u'UUID=%s %s %s defaults,errors=panic 0 0\n' %
(mount2uuid[fs.mount], fs.mount, fs.type))
else:
f.write(u'UUID=%s %s %s defaults 0 0\n' %
(mount2uuid[fs.mount], fs.mount, fs.type))
self.umount_target(chroot)
def do_reboot(self):
LOG.debug('--- Rebooting node (do_reboot) ---')
utils.execute('reboot')

View File

@ -0,0 +1,253 @@
# Copyright 2016 Mirantis, Inc.
#
# 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 six
import unittest2
from bareon.actions import bootloader
from bareon.drivers.data import nailgun
from bareon import errors
from bareon import objects
if six.PY2:
import mock
elif six.PY3:
import unittest.mock as mock
class TestBootLoaderAction(unittest2.TestCase):
def setUp(self):
super(TestBootLoaderAction, self).setUp()
self.drv = mock.MagicMock(spec=nailgun.Nailgun)
self.action = bootloader.BootLoaderAction(self.drv)
self.action._mount_target = mock.Mock()
self.action._umount_target = mock.Mock()
self.drv.grub = objects.Grub(
kernel_params=' console=ttyS0,9600 console=tty0 '
'rootdelay=90 nomodeset')
root_fs = objects.FS('/dev/sda', mount='/')
self.drv.partition_scheme.fss = [root_fs]
self.drv.partition_scheme.boot_device.return_value = '/dev/sda3'
parteds = [objects.Parted('/dev/sd%s' % x, 'gpt',
install_bootloader=True)
for x in ['a', 'b', 'c']]
self.drv.partition_scheme.parteds = parteds
@mock.patch.object(bootloader, 'open', create=True,
new_callable=mock.mock_open)
@mock.patch.object(bootloader, 'gu', autospec=True)
@mock.patch.object(bootloader, 'utils', autospec=True)
def test_do_bootloader_grub1_kernel_initrd_guessed(self, mock_utils,
mock_gu, mock_open):
mock_utils.execute.return_value = ('fake_root_uuid', '')
mock_gu.guess_grub_version.return_value = 1
# grub has kernel_name and initrd_name both set to None
self.drv.grub.kernel_name = None
self.drv.grub.initrd_name = None
self.drv.grub.kernel_params = 'fake_kernel_params'
self.drv.grub.kernel_regexp = 'fake_kernel_regexp'
self.drv.grub.initrd_regexp = 'fake_initrd_regexp'
mock_gu.guess_kernel.return_value = 'guessed_kernel'
mock_gu.guess_initrd.return_value = 'guessed_initrd'
self.action.execute()
self.assertFalse(mock_gu.grub2_cfg.called)
self.assertFalse(mock_gu.grub2_install.called)
mock_gu.grub1_cfg.assert_called_once_with(
kernel_params='fake_kernel_params root=UUID=fake_root_uuid ',
initrd='guessed_initrd', kernel='guessed_kernel',
chroot='/tmp/target', grub_timeout=5)
mock_gu.grub1_install.assert_called_once_with(
['/dev/sda', '/dev/sdb', '/dev/sdc'],
'/dev/sda3', chroot='/tmp/target')
mock_gu.guess_initrd.assert_called_once_with(
regexp='fake_initrd_regexp', chroot='/tmp/target')
mock_gu.guess_kernel.assert_called_once_with(
regexp='fake_kernel_regexp', chroot='/tmp/target')
@mock.patch.object(bootloader, 'open', create=True,
new_callable=mock.mock_open)
@mock.patch.object(bootloader, 'gu', autospec=True)
@mock.patch.object(bootloader, 'utils', autospec=True)
def test_do_bootloader_grub1_kernel_initrd_set(self, mock_utils,
mock_gu, mock_open):
mock_utils.execute.return_value = ('', '')
mock_gu.guess_grub_version.return_value = 1
self.drv.grub.kernel_params = 'fake_kernel_params'
# grub has kernel_name and initrd_name set
self.drv.grub.kernel_name = 'kernel_name'
self.drv.grub.initrd_name = 'initrd_name'
self.action.execute()
self.assertFalse(mock_gu.grub2_cfg.called)
self.assertFalse(mock_gu.grub2_install.called)
mock_gu.grub1_cfg.assert_called_once_with(
kernel_params='fake_kernel_params root=UUID= ',
initrd='initrd_name', kernel='kernel_name', chroot='/tmp/target',
grub_timeout=5)
mock_gu.grub1_install.assert_called_once_with(
['/dev/sda', '/dev/sdb', '/dev/sdc'],
'/dev/sda3', chroot='/tmp/target')
self.assertFalse(mock_gu.guess_initrd.called)
self.assertFalse(mock_gu.guess_kernel.called)
@mock.patch.object(objects, 'Grub', autospec=True)
@mock.patch.object(bootloader, 'open', create=True,
new_callable=mock.mock_open)
@mock.patch.object(bootloader, 'gu', autospec=True)
@mock.patch.object(bootloader, 'utils', autospec=True)
def test_do_bootloader_rootfs_uuid(self, mock_utils, mock_gu, mock_open,
mock_grub):
def _fake_uuid(*args, **kwargs):
if len(args) >= 6 and args[5] == '/dev/sda':
return ('FAKE_ROOTFS_UUID', None)
else:
return ('FAKE_UUID', None)
mock_utils.execute.side_effect = _fake_uuid
mock_grub.version = 2
mock_gu.guess_grub_version.return_value = 2
mock_grub.kernel_name = 'fake_kernel_name'
mock_grub.initrd_name = 'fake_initrd_name'
mock_grub.kernel_params = 'fake_kernel_params'
self.drv.grub = mock_grub
self.action.execute()
mock_grub.append_kernel_params.assert_called_once_with(
'root=UUID=FAKE_ROOTFS_UUID ')
self.assertEqual(2, mock_grub.version)
@mock.patch.object(bootloader, 'utils', autospec=True)
def test_do_bootloader_rootfs_not_found(self, mock_utils):
mock_utils.execute.return_value = ('fake', 'fake')
self.drv.partition_scheme.fss = [
objects.FS(device='fake', mount='/boot', fs_type='ext2'),
objects.FS(device='fake', mount='swap', fs_type='swap')]
self.assertRaises(errors.WrongPartitionSchemeError,
self.action.execute)
@mock.patch.object(bootloader, 'open', create=True,
new_callable=mock.mock_open)
@mock.patch.object(bootloader, 'gu', autospec=True)
@mock.patch.object(bootloader, 'utils', autospec=True)
def test_do_bootloader_grub_version_changes(
self, mock_utils, mock_gu, mock_open):
# actually covers only grub1 related logic
mock_utils.execute.return_value = ('fake_UUID\n', None)
mock_gu.guess_grub_version.return_value = 'expected_version'
self.action.execute()
mock_gu.guess_grub_version.assert_called_once_with(
chroot='/tmp/target')
self.assertEqual('expected_version', self.drv.grub.version)
@mock.patch.object(bootloader, 'open', create=True,
new_callable=mock.mock_open)
@mock.patch.object(bootloader, 'gu', autospec=True)
@mock.patch.object(bootloader, 'utils', autospec=True)
def test_do_bootloader_grub1(self, mock_utils, mock_gu, mock_open):
# actually covers only grub1 related logic
mock_utils.execute.return_value = ('fake_UUID\n', None)
mock_gu.guess_initrd.return_value = 'guessed_initrd'
mock_gu.guess_kernel.return_value = 'guessed_kernel'
mock_gu.guess_grub_version.return_value = 1
self.action.execute()
mock_gu.guess_grub_version.assert_called_once_with(
chroot='/tmp/target')
mock_gu.grub1_cfg.assert_called_once_with(
kernel_params=' console=ttyS0,9600 console=tty0 rootdelay=90 '
'nomodeset root=UUID=fake_UUID ',
initrd='guessed_initrd',
chroot='/tmp/target',
kernel='guessed_kernel',
grub_timeout=5)
mock_gu.grub1_install.assert_called_once_with(
['/dev/sda', '/dev/sdb', '/dev/sdc'],
'/dev/sda3', chroot='/tmp/target')
self.assertFalse(mock_gu.grub2_cfg.called)
self.assertFalse(mock_gu.grub2_install.called)
@mock.patch.object(bootloader, 'open', create=True,
new_callable=mock.mock_open)
@mock.patch.object(bootloader, 'gu', autospec=True)
@mock.patch.object(bootloader, 'utils', autospec=True)
def test_do_bootloader_grub2(self, mock_utils, mock_gu, mock_open):
# actually covers only grub2 related logic
mock_utils.execute.return_value = ('fake_UUID\n', None)
mock_gu.guess_grub_version.return_value = 2
self.action.execute()
mock_gu.guess_grub_version.assert_called_once_with(
chroot='/tmp/target')
mock_gu.grub2_cfg.assert_called_once_with(
kernel_params=' console=ttyS0,9600 console=tty0 rootdelay=90 '
'nomodeset root=UUID=fake_UUID ',
chroot='/tmp/target', grub_timeout=5)
mock_gu.grub2_install.assert_called_once_with(
['/dev/sda', '/dev/sdb', '/dev/sdc'],
chroot='/tmp/target')
self.assertFalse(mock_gu.grub1_cfg.called)
self.assertFalse(mock_gu.grub1_install.called)
@mock.patch.object(bootloader, 'gu', autospec=True)
@mock.patch.object(bootloader, 'utils', autospec=True)
def test_do_bootloader_writes(self, mock_utils, mock_gu):
# actually covers only write() calls
mock_utils.execute.return_value = ('fake_UUID\n', None)
self.drv.configdrive_scheme.common.udevrules = "08:00:27:79:da:80_"\
"eth0,08:00:27:46:43:60_eth1,08:00:27:b1:d7:15_eth2"
self.drv.partition_scheme.fss = [
objects.FS('device', mount='/boot', fs_type='ext2'),
objects.FS('device', mount='/tmp', fs_type='ext2'),
objects.FS('device', mount='/', fs_type='ext4'),
objects.FS('device', mount='swap', fs_type='swap'),
objects.FS('device', mount='/var/lib/glance')]
with mock.patch.object(bootloader, 'open', create=True) as mock_open:
file_handle_mock = mock_open.return_value.__enter__.return_value
self.action.execute()
expected_open_calls = [
mock.call('/tmp/target/etc/udev/rules.d/70-persistent-net.'
'rules', 'wt', encoding='utf-8'),
mock.call('/tmp/target/etc/udev/rules.d/75-persistent-net-'
'generator.rules', 'wt', encoding='utf-8'),
mock.call('/tmp/target/etc/nailgun-agent/nodiscover', 'w'),
mock.call('/tmp/target/etc/fstab', 'wt', encoding='utf-8')]
self.assertEqual(expected_open_calls, mock_open.call_args_list)
expected_write_calls = [
mock.call('# Generated by bareon during provisioning: '
'BEGIN\n'),
mock.call('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
'ATTR{address}=="08:00:27:79:da:80", ATTR{type}=="1"'
', KERNEL=="eth*", NAME="eth0"\n'),
mock.call('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
'ATTR{address}=="08:00:27:46:43:60", ATTR{type}=="1"'
', KERNEL=="eth*", NAME="eth1"\n'),
mock.call('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
'ATTR{address}=="08:00:27:b1:d7:15", ATTR{type}=="1"'
', KERNEL=="eth*", NAME="eth2"\n'),
mock.call('# Generated by bareon during provisioning: '
'END\n'),
mock.call('# Generated by bareon during provisioning:\n# '
'DO NOT DELETE. It is needed to disable '
'net-generator\n'),
mock.call('UUID=fake_UUID /boot ext2 defaults 0 0\n'),
mock.call('UUID=fake_UUID /tmp ext2 defaults 0 0\n'),
mock.call(
'UUID=fake_UUID / ext4 defaults,errors=panic 0 0\n'),
mock.call('UUID=fake_UUID swap swap defaults 0 0\n'),
mock.call('UUID=fake_UUID /var/lib/glance xfs defaults 0 0\n')
]
self.assertEqual(expected_write_calls,
file_handle_mock.write.call_args_list)
self.action._mount_target.assert_called_once_with(
'/tmp/target', os_id=None, pseudo=True, treat_mtab=True)
mock_utils.makedirs_if_not_exists.assert_called_once_with(
'/tmp/target/etc/nailgun-agent')
self.action._umount_target.assert_called_once_with(
'/tmp/target', os_id=None, pseudo=True)

View File

@ -21,11 +21,7 @@ import unittest2
import bareon
from bareon.drivers.data import nailgun as nailgun_data
from bareon.drivers.deploy import nailgun as nailgun_deploy
from bareon import errors
from bareon import objects
from bareon.tests import test_nailgun
from bareon.utils import hardware as hu
from bareon.utils import utils
if six.PY2:
@ -36,334 +32,6 @@ elif six.PY3:
CONF = cfg.CONF
@unittest2.skip("Fix after cray rebase")
class TestManager(unittest2.TestCase):
@mock.patch('bareon.drivers.data.nailgun.Nailgun.parse_image_meta',
return_value={})
@mock.patch.object(hu, 'list_block_devices')
def setUp(self, mock_lbd, mock_image_meta):
super(TestManager, self).setUp()
mock_lbd.return_value = test_nailgun.LIST_BLOCK_DEVICES_SAMPLE
self.mgr = nailgun_deploy.Manager(test_nailgun.PROVISION_SAMPLE_DATA)
@mock.patch('bareon.drivers.deploy.nailgun.open',
create=True, new_callable=mock.mock_open)
@mock.patch('bareon.drivers.deploy.nailgun.gu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch.object(bareon.drivers.deploy.nailgun, 'mount_target')
@mock.patch.object(bareon.drivers.deploy.nailgun, 'umount_target')
def test_do_bootloader_grub1_kernel_initrd_guessed(self, mock_umount,
mock_mount, mock_utils,
mock_gu, mock_open):
mock_utils.execute.return_value = ('', '')
mock_gu.guess_grub_version.return_value = 1
# grub has kernel_name and initrd_name both set to None
self.mgr.driver.grub.kernel_name = None
self.mgr.driver.grub.initrd_name = None
self.mgr.driver.grub.kernel_params = 'fake_kernel_params'
self.mgr.driver.grub.kernel_regexp = 'fake_kernel_regexp'
self.mgr.driver.grub.initrd_regexp = 'fake_initrd_regexp'
mock_gu.guess_kernel.return_value = 'guessed_kernel'
mock_gu.guess_initrd.return_value = 'guessed_initrd'
self.mgr.do_bootloader()
self.assertFalse(mock_gu.grub2_cfg.called)
self.assertFalse(mock_gu.grub2_install.called)
mock_gu.grub1_cfg.assert_called_once_with(
kernel_params='fake_kernel_params root=UUID= ',
initrd='guessed_initrd', kernel='guessed_kernel',
chroot='/tmp/target', grub_timeout=5)
mock_gu.grub1_install.assert_called_once_with(
['/dev/sda', '/dev/sdb', '/dev/sdc'],
'/dev/sda3', chroot='/tmp/target')
mock_gu.guess_initrd.assert_called_once_with(
regexp='fake_initrd_regexp', chroot='/tmp/target')
mock_gu.guess_kernel.assert_called_once_with(
regexp='fake_kernel_regexp', chroot='/tmp/target')
@mock.patch('bareon.drivers.deploy.nailgun.open',
create=True, new_callable=mock.mock_open)
@mock.patch('bareon.drivers.deploy.nailgun.gu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch.object(bareon.drivers.deploy.nailgun, 'mount_target')
@mock.patch.object(bareon.drivers.deploy.nailgun, 'umount_target')
def test_do_bootloader_grub1_kernel_initrd_set(self, mock_umount,
mock_mount, mock_utils,
mock_gu, mock_open):
mock_utils.execute.return_value = ('', '')
mock_gu.guess_grub_version.return_value = 1
self.mgr.driver.grub.kernel_params = 'fake_kernel_params'
# grub has kernel_name and initrd_name set
self.mgr.driver.grub.kernel_name = 'kernel_name'
self.mgr.driver.grub.initrd_name = 'initrd_name'
self.mgr.do_bootloader()
self.assertFalse(mock_gu.grub2_cfg.called)
self.assertFalse(mock_gu.grub2_install.called)
mock_gu.grub1_cfg.assert_called_once_with(
kernel_params='fake_kernel_params root=UUID= ',
initrd='initrd_name', kernel='kernel_name', chroot='/tmp/target',
grub_timeout=5)
mock_gu.grub1_install.assert_called_once_with(
['/dev/sda', '/dev/sdb', '/dev/sdc'],
'/dev/sda3', chroot='/tmp/target')
self.assertFalse(mock_gu.guess_initrd.called)
self.assertFalse(mock_gu.guess_kernel.called)
@mock.patch('bareon.objects.bootloader.Grub', autospec=True)
@mock.patch('bareon.drivers.deploy.nailgun.open',
create=True, new_callable=mock.mock_open)
@mock.patch('bareon.drivers.deploy.nailgun.gu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch.object(bareon.drivers.deploy.nailgun, 'mount_target')
@mock.patch.object(bareon.drivers.deploy.nailgun, 'umount_target')
def test_do_bootloader_rootfs_uuid(self, mock_umount, mock_mount,
mock_utils, mock_gu, mock_open,
mock_grub):
def _fake_uuid(*args, **kwargs):
if len(args) >= 6 and args[5] == '/dev/mapper/os-root':
return ('FAKE_ROOTFS_UUID', None)
else:
return ('FAKE_UUID', None)
mock_utils.execute.side_effect = _fake_uuid
mock_grub.version = 2
mock_gu.guess_grub_version.return_value = 2
mock_grub.kernel_name = 'fake_kernel_name'
mock_grub.initrd_name = 'fake_initrd_name'
mock_grub.kernel_params = 'fake_kernel_params'
self.mgr.driver._grub = mock_grub
self.mgr.do_bootloader()
mock_grub.append_kernel_params.assert_called_once_with(
'root=UUID=FAKE_ROOTFS_UUID ')
self.assertEqual(2, mock_grub.version)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch.object(bareon.drivers.deploy.nailgun, 'mount_target')
def test_do_bootloader_rootfs_not_found(self, mock_umount, mock_utils):
mock_utils.execute.return_value = ('fake', 'fake')
self.mgr.driver._partition_scheme = objects.PartitionScheme()
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/boot', fs_type='ext2')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='swap', fs_type='swap')
self.assertRaises(errors.WrongPartitionSchemeError,
self.mgr.do_bootloader)
@mock.patch('bareon.drivers.deploy.nailgun.open',
create=True, new_callable=mock.mock_open)
@mock.patch('bareon.drivers.deploy.nailgun.gu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch.object(bareon.drivers.deploy.nailgun, 'mount_target')
@mock.patch.object(bareon.drivers.deploy.nailgun, 'umount_target')
def test_do_bootloader_grub_version_changes(
self, mock_umount, mock_mount, mock_utils, mock_gu, mock_open):
# actually covers only grub1 related logic
mock_utils.execute.return_value = ('fake_UUID\n', None)
mock_gu.guess_grub_version.return_value = 'expected_version'
self.mgr.do_bootloader()
mock_gu.guess_grub_version.assert_called_once_with(
chroot='/tmp/target')
self.assertEqual('expected_version', self.mgr.driver.grub.version)
@mock.patch('bareon.drivers.deploy.nailgun.open',
create=True, new_callable=mock.mock_open)
@mock.patch('bareon.drivers.deploy.nailgun.gu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch.object(bareon.drivers.deploy.nailgun, 'mount_target')
@mock.patch.object(bareon.drivers.deploy.nailgun, 'umount_target')
def test_do_bootloader_grub1(self, mock_umount, mock_mount, mock_utils,
mock_gu, mock_open):
# actually covers only grub1 related logic
mock_utils.execute.return_value = ('fake_UUID\n', None)
mock_gu.guess_initrd.return_value = 'guessed_initrd'
mock_gu.guess_kernel.return_value = 'guessed_kernel'
mock_gu.guess_grub_version.return_value = 1
self.mgr.do_bootloader()
mock_gu.guess_grub_version.assert_called_once_with(
chroot='/tmp/target')
mock_gu.grub1_cfg.assert_called_once_with(
kernel_params=' console=ttyS0,9600 console=tty0 rootdelay=90 '
'nomodeset root=UUID=fake_UUID ',
initrd='guessed_initrd',
chroot='/tmp/target',
kernel='guessed_kernel',
grub_timeout=5)
mock_gu.grub1_install.assert_called_once_with(
['/dev/sda', '/dev/sdb', '/dev/sdc'],
'/dev/sda3', chroot='/tmp/target')
self.assertFalse(mock_gu.grub2_cfg.called)
self.assertFalse(mock_gu.grub2_install.called)
@mock.patch('bareon.drivers.deploy.nailgun.open',
create=True, new_callable=mock.mock_open)
@mock.patch('bareon.drivers.deploy.nailgun.gu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch.object(bareon.drivers.deploy.nailgun, 'mount_target')
@mock.patch.object(bareon.drivers.deploy.nailgun, 'umount_target')
def test_do_bootloader_grub2(self, mock_umount, mock_mount, mock_utils,
mock_gu, mock_open):
# actually covers only grub2 related logic
mock_utils.execute.return_value = ('fake_UUID\n', None)
mock_gu.guess_grub_version.return_value = 2
self.mgr.do_bootloader()
mock_gu.guess_grub_version.assert_called_once_with(
chroot='/tmp/target')
mock_gu.grub2_cfg.assert_called_once_with(
kernel_params=' console=ttyS0,9600 console=tty0 rootdelay=90 '
'nomodeset root=UUID=fake_UUID ',
chroot='/tmp/target', grub_timeout=5)
mock_gu.grub2_install.assert_called_once_with(
['/dev/sda', '/dev/sdb', '/dev/sdc'],
chroot='/tmp/target')
self.assertFalse(mock_gu.grub1_cfg.called)
self.assertFalse(mock_gu.grub1_install.called)
@mock.patch('bareon.drivers.deploy.nailgun.gu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch.object(bareon.drivers.deploy.nailgun, 'mount_target')
@mock.patch.object(bareon.drivers.deploy.nailgun, 'umount_target')
def test_do_bootloader_writes(self, mock_umount, mock_mount, mock_utils,
mock_gu):
# actually covers only write() calls
mock_utils.execute.return_value = ('fake_UUID\n', None)
with mock.patch('bareon.drivers.deploy.nailgun.open',
create=True) as mock_open:
file_handle_mock = mock_open.return_value.__enter__.return_value
self.mgr.do_bootloader()
expected_open_calls = [
mock.call('/tmp/target/etc/udev/rules.d/70-persistent-net.'
'rules', 'wt', encoding='utf-8'),
mock.call('/tmp/target/etc/udev/rules.d/75-persistent-net-'
'generator.rules', 'wt', encoding='utf-8'),
mock.call('/tmp/target/etc/nailgun-agent/nodiscover', 'w'),
mock.call('/tmp/target/etc/fstab', 'wt', encoding='utf-8')]
self.assertEqual(expected_open_calls, mock_open.call_args_list)
expected_write_calls = [
mock.call('# Generated by bareon during provisioning: '
'BEGIN\n'),
mock.call('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
'ATTR{address}=="08:00:27:79:da:80", ATTR{type}=="1"'
', KERNEL=="eth*", NAME="eth0"\n'),
mock.call('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
'ATTR{address}=="08:00:27:46:43:60", ATTR{type}=="1"'
', KERNEL=="eth*", NAME="eth1"\n'),
mock.call('SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", '
'ATTR{address}=="08:00:27:b1:d7:15", ATTR{type}=="1"'
', KERNEL=="eth*", NAME="eth2"\n'),
mock.call('# Generated by bareon during provisioning: '
'END\n'),
mock.call('# Generated by bareon during provisioning:\n# '
'DO NOT DELETE. It is needed to disable '
'net-generator\n'),
mock.call('UUID=fake_UUID /boot ext2 defaults 0 0\n'),
mock.call('UUID=fake_UUID /tmp ext2 defaults 0 0\n'),
mock.call(
'UUID=fake_UUID / ext4 defaults,errors=panic 0 0\n'),
mock.call('UUID=fake_UUID swap swap defaults 0 0\n'),
mock.call('UUID=fake_UUID /var/lib/glance xfs defaults 0 0\n')
]
self.assertEqual(expected_write_calls,
file_handle_mock.write.call_args_list)
mock_umount.assert_called_once_with('/tmp/target')
mock_mount.assert_called_once_with('/tmp/target')
mock_utils.makedirs_if_not_exists.assert_called_once_with(
'/tmp/target/etc/nailgun-agent')
@mock.patch('bareon.drivers.deploy.nailgun.fu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.open',
create=True, new_callable=mock.mock_open)
@mock.patch('bareon.drivers.deploy.nailgun.os', create=True)
def test_mount_target_mtab_is_link(self, mock_os, mock_open, mock_utils,
mock_fu):
mock_os.path.islink.return_value = True
mock_utils.execute.return_value = (None, None)
self.mgr.driver._partition_scheme = objects.PartitionScheme()
self.mgr.mount_target('fake_chroot')
mock_open.assert_called_once_with('fake_chroot/etc/mtab', 'wt',
encoding='utf-8')
mock_os.path.islink.assert_called_once_with('fake_chroot/etc/mtab')
mock_os.remove.assert_called_once_with('fake_chroot/etc/mtab')
@mock.patch('bareon.drivers.deploy.nailgun.fu', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.utils', create=True)
@mock.patch('bareon.drivers.deploy.nailgun.open',
create=True, new_callable=mock.mock_open)
@mock.patch('bareon.drivers.deploy.nailgun.os', create=True)
def test_mount_target(self, mock_os, mock_open, mock_utils, mock_fu):
mock_os.path.islink.return_value = False
self.mgr.driver._partition_scheme = objects.PartitionScheme()
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/var/lib', fs_type='xfs')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/', fs_type='ext4')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/boot', fs_type='ext2')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='swap', fs_type='swap')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/var', fs_type='ext4')
fake_mtab = """
proc /proc proc rw,noexec,nosuid,nodev 0 0
sysfs /sys sysfs rw,noexec,nosuid,nodev 0 0
none /sys/fs/fuse/connections fusectl rw 0 0
none /sys/kernel/debug debugfs rw 0 0
none /sys/kernel/security securityfs rw 0 0
udev /dev devtmpfs rw,mode=0755 0 0
devpts /dev/pts devpts rw,noexec,nosuid,gid=5,mode=0620 0 0
tmpfs /run tmpfs rw,noexec,nosuid,size=10%,mode=0755 0 0
none /run/lock tmpfs rw,noexec,nosuid,nodev,size=5242880 0 0
none /run/shm tmpfs rw,nosuid,nodev 0 0"""
mock_utils.execute.return_value = (fake_mtab, None)
self.mgr.mount_target('fake_chroot')
self.assertEqual([mock.call('fake_chroot/'),
mock.call('fake_chroot/boot'),
mock.call('fake_chroot/var'),
mock.call('fake_chroot/var/lib'),
mock.call('fake_chroot/sys'),
mock.call('fake_chroot/dev'),
mock.call('fake_chroot/proc')],
mock_utils.makedirs_if_not_exists.call_args_list)
self.assertEqual([mock.call('ext4', 'fake', 'fake_chroot/'),
mock.call('ext2', 'fake', 'fake_chroot/boot'),
mock.call('ext4', 'fake', 'fake_chroot/var'),
mock.call('xfs', 'fake', 'fake_chroot/var/lib')],
mock_fu.mount_fs.call_args_list)
self.assertEqual([mock.call('fake_chroot', '/sys'),
mock.call('fake_chroot', '/dev'),
mock.call('fake_chroot', '/proc')],
mock_fu.mount_bind.call_args_list)
file_handle = mock_open.return_value.__enter__.return_value
file_handle.write.assert_called_once_with(fake_mtab)
mock_open.assert_called_once_with('fake_chroot/etc/mtab', 'wt',
encoding='utf-8')
mock_os.path.islink.assert_called_once_with('fake_chroot/etc/mtab')
self.assertFalse(mock_os.remove.called)
@mock.patch('bareon.drivers.deploy.nailgun.fu', create=True)
def test_umount_target(self, mock_fu):
self.mgr.driver._partition_scheme = objects.PartitionScheme()
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/var/lib', fs_type='xfs')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/', fs_type='ext4')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/boot', fs_type='ext2')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='swap', fs_type='swap')
self.mgr.driver.partition_scheme.add_fs(
device='fake', mount='/var', fs_type='ext4')
self.mgr.umount_target('fake_chroot')
self.assertEqual([mock.call('fake_chroot/proc'),
mock.call('fake_chroot/dev'),
mock.call('fake_chroot/sys/fs/fuse/connections'),
mock.call('fake_chroot/sys'),
mock.call('fake_chroot/var/lib'),
mock.call('fake_chroot/boot'),
mock.call('fake_chroot/var'),
mock.call('fake_chroot/')],
mock_fu.umount_fs.call_args_list)
@unittest2.skip("Fix after cray rebase")
class TestImageBuild(unittest2.TestCase):

View File

@ -43,6 +43,7 @@ bareon.actions =
do_partitioning = bareon.actions.partitioning:PartitioningAction
do_configdrive = bareon.actions.configdrive:ConfigDriveAction
do_copyimage = bareon.actions.copyimage:CopyImageAction
do_bootloader = bareon.actions.bootloader:BootLoaderAction
oslo.config.opts =
bareon.manager = bareon.manager:list_opts