mirror of
https://github.com/gryf/openstack.git
synced 2025-12-17 03:20:25 +01:00
218 lines
9.0 KiB
Diff
218 lines
9.0 KiB
Diff
From be717da52b717b9a59f947e3ade2be5423d401a4 Mon Sep 17 00:00:00 2001
|
|
From: "Grzegorz Grasza (xek)" <grzegorz.grasza@intel.com>
|
|
Date: Fri, 19 Jan 2018 13:29:30 +0100
|
|
Subject: [PATCH 07/11] System installation implementation
|
|
|
|
Co-Authored-By: Grzegorz Grasza <grzegorz.grasza@intel.com>
|
|
---
|
|
ironic_lib/system_installer/systemsetup.py | 93 ++++++++++++++++++++++++++++++
|
|
ironic_lib/tests/test_systemsetup.py | 90 +++++++++++++++++++++++++++++
|
|
2 files changed, 183 insertions(+)
|
|
create mode 100644 ironic_lib/tests/test_systemsetup.py
|
|
|
|
diff --git a/ironic_lib/system_installer/systemsetup.py b/ironic_lib/system_installer/systemsetup.py
|
|
index e03c5b4..fa303e6 100644
|
|
--- a/ironic_lib/system_installer/systemsetup.py
|
|
+++ b/ironic_lib/system_installer/systemsetup.py
|
|
@@ -13,8 +13,101 @@
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
+import os
|
|
+
|
|
+from ironic_lib import utils
|
|
+from oslo_log import log
|
|
+
|
|
+from ironic_lib.system_installer import exceptions
|
|
from ironic_lib.system_installer.filesystemsetup import FilesystemSetup
|
|
|
|
+LOG = log.getLogger()
|
|
+
|
|
|
|
class SystemSetup(FilesystemSetup):
|
|
"""Basic disk formatting implementation"""
|
|
+
|
|
+ def validate_env(self):
|
|
+ if not self._cmd_exists('guestmount'):
|
|
+ raise exceptions.EnvError('guestmount command not available')
|
|
+
|
|
+ def setup_disks(self, devices, image_path):
|
|
+ fstab = []
|
|
+ for disk_name, disk in sorted(
|
|
+ self.conf.items(), key=lambda x: x[1].get('mountpoint', 'z')):
|
|
+ if 'mountpoint' in disk and not disk.get('preserve', False):
|
|
+ mountpoint = disk['mountpoint']
|
|
+ device = devices.get(disk_name, disk_name)
|
|
+ try:
|
|
+ os.makedirs('/next' + mountpoint)
|
|
+ except OSError as e:
|
|
+ if e.errno != os.errno.EEXIST:
|
|
+ raise
|
|
+ out, err = utils.execute('mount', device, '/next' + mountpoint)
|
|
+ LOG.debug("mount stdout: %s", out)
|
|
+ LOG.debug("mount stderr: %s", err)
|
|
+ fstab.append(self._generate_fstab_line(
|
|
+ device, disk.get('mountpoint'), disk.get('fstype', 'xfs'),
|
|
+ disk.get('mountopts')))
|
|
+ self._copy_files(image_path)
|
|
+ for m in ['/dev', '/dev/pts', '/proc', '/sys', '/run']:
|
|
+ utils.execute('mount', '-B', m, '/next' + m)
|
|
+ self._write_fstab(fstab)
|
|
+ partition_label = self.get_boot_partition()
|
|
+ return self._install_bootloader(devices.get(partition_label,
|
|
+ partition_label))
|
|
+
|
|
+ def _generate_fstab_line(self, device, mountpoint, fstype, mountopts):
|
|
+ if not mountpoint and fstype != 'swap':
|
|
+ return ''
|
|
+ uuid = self._get_uuid(device)
|
|
+ opts = 'defaults'
|
|
+ dump = '0'
|
|
+ pas = '2'
|
|
+ if fstype == 'vfat':
|
|
+ opts = 'umask=0077'
|
|
+ if mountpoint in ['/', '/boot']:
|
|
+ pas = '1'
|
|
+ elif fstype == 'swap':
|
|
+ opts = 'sw'
|
|
+ pas = '0'
|
|
+ mountpoint = 'none'
|
|
+ return 'UUID=' + '\t'.join(
|
|
+ [uuid, mountpoint, fstype, mountopts or opts, dump, pas]) + '\n'
|
|
+
|
|
+ def _copy_files(self, image_path):
|
|
+ try:
|
|
+ os.mkdir('/image')
|
|
+ except OSError as e:
|
|
+ if e.errno != os.errno.EEXIST:
|
|
+ raise
|
|
+ utils.execute('guestmount', '-a', image_path, '-i', '--ro', '/image')
|
|
+ utils.execute('cp', '-a', '/image/.', '/next/')
|
|
+ utils.execute('umount', '-R', '/image')
|
|
+
|
|
+ def _write_fstab(self, fstab_list):
|
|
+ if ''.join(fstab_list).strip():
|
|
+ with open('/next/etc/fstab', 'w') as fstab:
|
|
+ fstab.write('# generated by disk_partitioner.py\n')
|
|
+ fstab.writelines(fstab_list)
|
|
+
|
|
+ def _install_bootloader(self, device):
|
|
+ device = device.rstrip('1234567890') # install to MBR
|
|
+ if os.path.isfile('/next/usr/sbin/grub-install'):
|
|
+ grub = 'grub'
|
|
+ elif os.path.isfile('/next/usr/sbin/grub2-install'):
|
|
+ grub = 'grub2'
|
|
+ else:
|
|
+ raise exceptions.EnvError('grub command not available on image')
|
|
+ utils.execute('chroot /next /usr/sbin/{}-mkconfig'
|
|
+ ' -o /boot/{}/grub.cfg'.format(grub, grub), shell=True)
|
|
+ utils.execute('chroot /next /usr/sbin/{}-install --recheck {}'.format(
|
|
+ grub, device), shell=True)
|
|
+ utils.execute('sync')
|
|
+ utils.execute('umount', '-R', '/next')
|
|
+ return device
|
|
+
|
|
+ @staticmethod
|
|
+ def _get_uuid(device):
|
|
+ out, _ = utils.execute('blkid -o value -s UUID ' + device, shell=True)
|
|
+ return out.strip()
|
|
diff --git a/ironic_lib/tests/test_systemsetup.py b/ironic_lib/tests/test_systemsetup.py
|
|
new file mode 100644
|
|
index 0000000..0e127f5
|
|
--- /dev/null
|
|
+++ b/ironic_lib/tests/test_systemsetup.py
|
|
@@ -0,0 +1,90 @@
|
|
+# Copyright (c) 2018 Intel Corporation
|
|
+#
|
|
+# 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 unittest
|
|
+
|
|
+import mock
|
|
+
|
|
+from ironic_lib.system_installer import exceptions
|
|
+from ironic_lib.system_installer import systemsetup
|
|
+
|
|
+
|
|
+class SystemSetupTestCase(unittest.TestCase):
|
|
+
|
|
+ @mock.patch.object(systemsetup.SystemSetup, '_cmd_exists')
|
|
+ def test_validate_env_fail(self, cmd):
|
|
+ system_setup = systemsetup.SystemSetup({'filesystems': {}})
|
|
+ cmd.return_value = False
|
|
+ with self.assertRaises(exceptions.EnvError):
|
|
+ system_setup.validate_env()
|
|
+
|
|
+ @mock.patch('ironic_lib.system_installer.systemsetup.open')
|
|
+ @mock.patch('ironic_lib.utils.execute')
|
|
+ @mock.patch.object(systemsetup.SystemSetup, '_get_uuid')
|
|
+ @mock.patch('os.path.isfile')
|
|
+ @mock.patch('os.mkdir')
|
|
+ def test_setup_disks(self, mkdir, is_file, uuid, exe_mock, mock_open):
|
|
+ uuid.return_value = 'test_uuid'
|
|
+ exe_mock.return_value = 'grub-install (GRUB) 2.02~beta3-4ubuntu2.2', 0
|
|
+
|
|
+ system_setup = systemsetup.SystemSetup(
|
|
+ {'filesystems': {'boot': {'mountpoint': '/boot',
|
|
+ 'mountopts': 'foo',
|
|
+ 'fstype': 'ext2'},
|
|
+ 'root': {'mountpoint': '/',
|
|
+ 'fstype': 'ext4'},
|
|
+ 'home': {'mountpoint': '/home'}}})
|
|
+ system_setup.setup_disks({}, 'test_image')
|
|
+ exe_mock.assert_any_call('mount', 'boot', '/next/boot')
|
|
+ file_handle = mock_open.return_value.__enter__.return_value
|
|
+ file_handle.writelines.assert_called_with(
|
|
+ ['UUID=test_uuid\t/\text4\tdefaults\t0\t1\n',
|
|
+ 'UUID=test_uuid\t/boot\text2\tfoo\t0\t1\n',
|
|
+ 'UUID=test_uuid\t/home\txfs\tdefaults\t0\t2\n'])
|
|
+
|
|
+ @mock.patch.object(systemsetup.SystemSetup, '_install_bootloader')
|
|
+ @mock.patch.object(systemsetup.SystemSetup, '_copy_files')
|
|
+ @mock.patch('ironic_lib.system_installer.systemsetup.open')
|
|
+ @mock.patch('ironic_lib.utils.execute')
|
|
+ @mock.patch.object(systemsetup.SystemSetup, '_get_uuid')
|
|
+ @mock.patch('os.path.isfile')
|
|
+ @mock.patch('os.mkdir')
|
|
+ def test_setup_disks_preserve_true(self, mkdir, is_file,
|
|
+ uuid, exe_mock, mock_open,
|
|
+ copy, bootloader):
|
|
+ uuid.return_value = 'test_uuid'
|
|
+ exe_mock.return_value = 'grub-install (GRUB) 2.02~beta3-4ubuntu2.2', 0
|
|
+
|
|
+ system_setup = systemsetup.SystemSetup(
|
|
+ {'filesystems': {'boot': {'mountpoint': '/boot',
|
|
+ 'mountopts': 'foo',
|
|
+ 'fstype': 'ext2'},
|
|
+ 'home': {'mountpoint': '/home',
|
|
+ 'preserve': 1}}})
|
|
+ system_setup.setup_disks({}, 'test_image')
|
|
+ exe_mock.assert_any_call('mount', 'boot', '/next/boot')
|
|
+ exe_mock.assert_any_call('mount', '-B', '/dev', '/next' + '/dev')
|
|
+ exe_mock.assert_any_call('mount', '-B', '/dev/pts',
|
|
+ '/next' + '/dev/pts')
|
|
+ exe_mock.assert_any_call('mount', '-B', '/proc', '/next' + '/proc')
|
|
+ exe_mock.assert_any_call('mount', '-B', '/sys', '/next' + '/sys')
|
|
+ exe_mock.assert_any_call('mount', '-B', '/run', '/next' + '/run')
|
|
+
|
|
+ assert exe_mock.call_count == 6
|
|
+
|
|
+ file_handle = mock_open.return_value.__enter__.return_value
|
|
+ file_handle.writelines.assert_called_with(
|
|
+ ['UUID=test_uuid\t/boot\text2\tfoo\t0\t1\n',
|
|
+ 'UUID=test_uuid\t/home\txfs\tdefaults\t0\t2\n'])
|
|
--
|
|
2.14.1
|
|
|