class TestSystemSetup(object): @patch('platform.machine') def setup(self, mock_machine): mock_machine.return_value = 'x86_64' self.context_manager_mock = mock.Mock() self.file_mock = mock.Mock() self.enter_mock = mock.Mock() self.exit_mock = mock.Mock() self.enter_mock.return_value = self.file_mock setattr(self.context_manager_mock, '__enter__', self.enter_mock) setattr(self.context_manager_mock, '__exit__', self.exit_mock) self.xml_state = mock.MagicMock() self.xml_state.get_package_manager = mock.Mock( return_value='zypper' ) self.xml_state.build_type.get_filesystem = mock.Mock( return_value='ext3' ) self.xml_state.xml_data.get_name = mock.Mock( return_value='some-image' ) self.xml_state.get_image_version = mock.Mock( return_value='1.2.3' ) self.xml_state.xml_data.description_dir = 'description_dir' self.setup = SystemSetup( self.xml_state, 'root_dir' ) description = XMLDescription( description='../data/example_config.xml', derived_from='derived/description' ) self.setup_with_real_xml = SystemSetup( XMLState(description.load()), 'root_dir' ) command_run = namedtuple( 'command', ['output', 'error', 'returncode'] ) self.run_result = command_run( output='password-hash\n', error='stderr', returncode=0 ) @patch('platform.machine') def test_setup_ix86(self, mock_machine): mock_machine.return_value = 'i686' setup = SystemSetup( mock.MagicMock(), 'root_dir' ) assert setup.arch == 'ix86' @patch('kiwi.command.Command.run') @patch_open @patch('os.path.exists') @patch('kiwi.system.setup.glob.iglob') def test_import_description( self, mock_iglob, mock_path, mock_open, mock_command ): mock_iglob.return_value = ['config-cdroot.tar.xz'] mock_path.return_value = True self.setup_with_real_xml.import_description() mock_iglob.assert_called_once_with( '../data/config-cdroot.tar*' ) assert mock_command.call_args_list == [ call(['mkdir', '-p', 'root_dir/image']), call(['cp', '../data/config.sh', 'root_dir/image/config.sh']), call([ 'cp', '../data/my_edit_boot_script', 'root_dir/image/edit_boot_config.sh' ]), call([ 'cp', '/absolute/path/to/my_edit_boot_install', 'root_dir/image/edit_boot_install.sh' ]), call(['cp', '../data/images.sh', 'root_dir/image/images.sh']), call([ 'cp', Defaults.project_file('config/functions.sh'), 'root_dir/.kconfig' ]), call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']), call(['cp', '../data/bootstrap.tgz', 'root_dir/image/']), call(['cp', 'config-cdroot.tar.xz', 'root_dir/image/']) ] @patch('kiwi.command.Command.run') @patch_open @patch('os.path.exists') def test_import_description_archive_from_derived( self, mock_path, mock_open, mock_command ): path_return_values = [ True, False, True, True, True, True, True ] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect self.setup_with_real_xml.import_description() assert mock_command.call_args_list == [ call(['mkdir', '-p', 'root_dir/image']), call(['cp', '../data/config.sh', 'root_dir/image/config.sh']), call([ 'cp', '../data/my_edit_boot_script', 'root_dir/image/edit_boot_config.sh' ]), call([ 'cp', '/absolute/path/to/my_edit_boot_install', 'root_dir/image/edit_boot_install.sh' ]), call(['cp', '../data/images.sh', 'root_dir/image/images.sh']), call([ 'cp', Defaults.project_file('config/functions.sh'), 'root_dir/.kconfig' ]), call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']), call([ 'cp', 'derived/description/bootstrap.tgz', 'root_dir/image/' ]) ] @patch('kiwi.command.Command.run') @patch_open @patch('os.path.exists') @raises(KiwiImportDescriptionError) def test_import_description_configured_editboot_scripts_not_found( self, mock_path, mock_open, mock_command ): path_return_values = [False, True, True] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect self.setup_with_real_xml.import_description() @patch('kiwi.command.Command.run') @patch_open @patch('os.path.exists') @raises(KiwiImportDescriptionError) def test_import_description_configured_archives_not_found( self, mock_path, mock_open, mock_command ): path_return_values = [False, False, True, True, True, True] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect self.setup_with_real_xml.import_description() @patch('kiwi.command.Command.run') def test_cleanup(self, mock_command): self.setup.cleanup() mock_command.assert_called_once_with( ['rm', '-r', '-f', '/.kconfig', '/image'] ) @patch_open def test_import_shell_environment(self, mock_open): mock_profile = mock.MagicMock() mock_profile.create = mock.Mock( return_value=['a'] ) mock_open.return_value = self.context_manager_mock self.setup.import_shell_environment(mock_profile) mock_profile.create.assert_called_once_with() mock_open.assert_called_once_with('root_dir/.profile', 'w') self.file_mock.write.assert_called_once_with('a\n') @patch('kiwi.system.setup.ArchiveTar') @patch('kiwi.system.setup.glob.iglob') def test_import_cdroot_files(self, mock_iglob, mock_ArchiveTar): archive = mock.Mock() mock_ArchiveTar.return_value = archive mock_iglob.return_value = ['config-cdroot.tar.xz'] self.setup.import_cdroot_files('target_dir') mock_iglob.assert_called_once_with('description_dir/config-cdroot.tar*') mock_ArchiveTar.assert_called_once_with('config-cdroot.tar.xz') archive.extract.assert_called_once_with('target_dir') @patch('kiwi.command.Command.run') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_import_overlay_files_copy_links( self, mock_os_path, mock_DataSync, mock_command ): data = mock.Mock() mock_DataSync.return_value = data mock_os_path.return_value = True self.setup.import_overlay_files( follow_links=True, preserve_owner_group=True ) mock_DataSync.assert_called_once_with( 'description_dir/root/', 'root_dir' ) data.sync_data.assert_called_once_with( options=[ '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system', '--copy-links', '-o', '-g' ] ) @patch('kiwi.command.Command.run') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_import_overlay_files_links( self, mock_os_path, mock_DataSync, mock_command ): data = mock.Mock() mock_DataSync.return_value = data mock_os_path.return_value = True self.setup.import_overlay_files( follow_links=False, preserve_owner_group=True ) mock_DataSync.assert_called_once_with( 'description_dir/root/', 'root_dir' ) data.sync_data.assert_called_once_with( options=[ '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system', '--links', '-o', '-g' ] ) @patch('kiwi.system.setup.ArchiveTar') @patch('os.path.exists') def test_import_overlay_files_from_archive( self, mock_os_path, mock_archive ): archive = mock.Mock() mock_archive.return_value = archive exists_results = [True, False] def side_effect(arg): return exists_results.pop() mock_os_path.side_effect = side_effect self.setup.import_overlay_files() mock_archive.assert_called_once_with( 'description_dir/root.tar.gz' ) archive.extract.assert_called_once_with( 'root_dir' ) @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_keyboard_map(self, mock_path, mock_run, mock_shell): mock_path.return_value = True self.setup.preferences['keytable'] = 'keytable' self.setup.setup_keyboard_map() mock_shell.assert_called_once_with( 'baseUpdateSysConfig', [ 'root_dir/etc/sysconfig/keyboard', 'KEYTABLE', '"keytable"' ] ) @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_keyboard_map_with_systemd( self, mock_path, mock_run, mock_shell, mock_caps ): mock_caps.return_value = True mock_path.return_value = True self.setup.preferences['keytable'] = 'keytable' self.setup.setup_keyboard_map() mock_run.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/vconsole.conf']), call([ 'chroot', 'root_dir', 'systemd-firstboot', '--keymap=keytable' ]) ]) @patch('kiwi.logger.log.warning') @patch('os.path.exists') def test_setup_keyboard_skipped(self, mock_exists, mock_log_warn): mock_exists.return_value = False self.setup.preferences['keytable'] = 'keytable' self.setup.setup_keyboard_map() assert mock_log_warn.called @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_locale(self, mock_path, mock_run, mock_shell): mock_path.return_value = True self.setup.preferences['locale'] = 'locale1,locale2' self.setup.setup_locale() mock_shell.assert_called_once_with( 'baseUpdateSysConfig', [ 'root_dir/etc/sysconfig/language', 'RC_LANG', 'locale1.UTF-8' ] ) @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_locale_with_systemd( self, mock_path, mock_run, mock_shell, mock_caps ): mock_caps.return_valure = True mock_path.return_value = True self.setup.preferences['locale'] = 'locale1,locale2' self.setup.setup_locale() mock_run.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/locale.conf']), call([ 'chroot', 'root_dir', 'systemd-firstboot', '--locale=locale1.UTF-8' ]) ]) @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_locale_POSIX(self, mock_path, mock_run, mock_shell): mock_path.return_value = True self.setup.preferences['locale'] = 'POSIX,locale2' self.setup.setup_locale() mock_shell.assert_called_once_with( 'baseUpdateSysConfig', [ 'root_dir/etc/sysconfig/language', 'RC_LANG', 'POSIX' ] ) @patch('kiwi.logger.log.warning') @patch('os.path.exists') def test_setup_locale_skipped(self, mock_exists, mock_log_warn): mock_exists.return_value = False self.setup.preferences['locale'] = 'locale1,locale2' self.setup.setup_locale() assert mock_log_warn.called @patch('kiwi.system.setup.Command.run') def test_setup_timezone(self, mock_command): self.setup.preferences['timezone'] = 'timezone' self.setup.setup_timezone() mock_command.assert_has_calls([ call([ 'chroot', 'root_dir', 'ln', '-s', '-f', '/usr/share/zoneinfo/timezone', '/etc/localtime' ]) ]) @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Command.run') def test_setup_timezone_with_systemd(self, mock_command, mock_caps): mock_caps.return_value = True self.setup.preferences['timezone'] = 'timezone' self.setup.setup_timezone() mock_command.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/localtime']), call([ 'chroot', 'root_dir', 'systemd-firstboot', '--timezone=timezone' ]) ]) @patch('kiwi.system.setup.Users') def test_setup_groups(self, mock_users): users = mock.Mock() users.group_exists = mock.Mock( return_value=False ) mock_users.return_value = users self.setup_with_real_xml.setup_groups() calls = [ call('users'), call('kiwi'), call('admin') ] users.group_exists.assert_has_calls(calls) calls = [ call('users', []), call('kiwi', []), call('admin', []) ] users.group_add.assert_has_calls(calls) @patch('kiwi.system.setup.Users') @patch('kiwi.system.setup.Command.run') def test_setup_users_add(self, mock_command, mock_users): users = mock.Mock() users.user_exists = mock.Mock( return_value=False ) mock_users.return_value = users mock_command.return_value = self.run_result self.setup_with_real_xml.setup_users() calls = [ call('root'), call('tux'), call('kiwi') ] users.user_exists.assert_has_calls(calls) calls = [ call( 'root', [ '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c', 'Bob', '-m', '-d', '/root' ] ), call( 'tux', [ '-p', 'password-hash', '-g', 'users', '-m', '-d', '/home/tux' ] ), call( 'kiwi', [ '-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users', '-m', '-d', '/home/kiwi' ] ) ] users.user_add.assert_has_calls(calls) mock_command.assert_called_with( ['openssl', 'passwd', '-1', '-salt', 'xyz', 'mypwd'] ) @patch('kiwi.system.setup.Users') @patch('kiwi.system.setup.Command.run') def test_setup_users_modify(self, mock_command, mock_users): users = mock.Mock() users.user_exists = mock.Mock( return_value=True ) mock_users.return_value = users mock_command.return_value = self.run_result self.setup_with_real_xml.setup_users() calls = [ call('root'), call('tux'), call('kiwi') ] users.user_exists.assert_has_calls(calls) calls = [ call( 'root', [ '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c', 'Bob' ] ), call( 'tux', [ '-p', 'password-hash', '-g', 'users' ] ), call( 'kiwi', [ '-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users' ] ) ] users.user_modify.assert_has_calls(calls) @patch('kiwi.system.setup.Path.which') @patch('kiwi.system.setup.Command.run') def test_setup_plymouth_splash(self, mock_command, mock_which): mock_which.return_value = 'plymouth-set-default-theme' preferences = mock.Mock() preferences.get_bootsplash_theme = mock.Mock( return_value=['some-theme'] ) self.xml_state.get_preferences_sections = mock.Mock( return_value=[preferences] ) self.setup.setup_plymouth_splash() mock_which.assert_called_once_with( custom_env={'PATH': 'root_dir/usr/sbin'}, filename='plymouth-set-default-theme' ) mock_command.assert_called_once_with( ['chroot', 'root_dir', 'plymouth-set-default-theme', 'some-theme'] ) @patch_open @patch('os.path.exists') def test_import_image_identifier(self, mock_os_path, mock_open): self.xml_state.xml_data.get_id = mock.Mock( return_value='42' ) mock_os_path.return_value = True mock_open.return_value = self.context_manager_mock self.setup.import_image_identifier() mock_open.assert_called_once_with('root_dir/etc/ImageID', 'w') self.file_mock.write.assert_called_once_with('42\n') @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') def test_call_config_script(self, mock_os_path, mock_watch, mock_command): result_type = namedtuple( 'result', ['stderr', 'returncode'] ) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result self.setup.call_config_script() mock_command.assert_called_once_with( ['chroot', 'root_dir', 'bash', '/image/config.sh'] ) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') def test_call_image_script(self, mock_os_path, mock_watch, mock_command): result_type = namedtuple( 'result_type', ['stderr', 'returncode'] ) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result self.setup.call_image_script() mock_command.assert_called_once_with( ['chroot', 'root_dir', 'bash', '/image/images.sh'] ) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.path.abspath') def test_call_edit_boot_config_script( self, mock_abspath, mock_exists, mock_watch, mock_command ): result_type = namedtuple( 'result_type', ['stderr', 'returncode'] ) mock_result = result_type(stderr='stderr', returncode=0) mock_exists.return_value = True mock_abspath.return_value = '/root_dir/image/edit_boot_config.sh' mock_watch.return_value = mock_result self.setup.call_edit_boot_config_script('ext4', 1) mock_abspath.assert_called_once_with( 'root_dir/image/edit_boot_config.sh' ) mock_command.assert_called_once_with([ 'bash', '-c', 'cd root_dir && bash --norc /root_dir/image/edit_boot_config.sh ext4 1' ]) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.path.abspath') def test_call_edit_boot_install_script( self, mock_abspath, mock_exists, mock_watch, mock_command ): result_type = namedtuple( 'result_type', ['stderr', 'returncode'] ) mock_result = result_type(stderr='stderr', returncode=0) mock_exists.return_value = True mock_abspath.return_value = '/root_dir/image/edit_boot_install.sh' mock_watch.return_value = mock_result self.setup.call_edit_boot_install_script( 'my_image.raw', '/dev/mapper/loop0p1' ) mock_abspath.assert_called_once_with( 'root_dir/image/edit_boot_install.sh' ) mock_command.assert_called_once_with([ 'bash', '-c', 'cd root_dir && bash --norc /root_dir/image/edit_boot_install.sh my_image.raw /dev/mapper/loop0p1' ]) @raises(KiwiScriptFailed) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') def test_call_image_script_raises( self, mock_os_path, mock_watch, mock_command ): result_type = namedtuple( 'result_type', ['stderr', 'returncode'] ) mock_result = result_type(stderr='stderr', returncode=1) mock_os_path.return_value = True mock_watch.return_value = mock_result self.setup.call_image_script() @raises(KiwiScriptFailed) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') def test_call_edit_boot_install_script_raises( self, mock_os_path, mock_watch, mock_command ): result_type = namedtuple( 'result_type', ['stderr', 'returncode'] ) mock_result = result_type(stderr='stderr', returncode=1) mock_os_path.return_value = True mock_watch.return_value = mock_result self.setup.call_edit_boot_install_script( 'my_image.raw', '/dev/mapper/loop0p1' ) @patch('kiwi.command.Command.run') def test_create_init_link_from_linuxrc(self, mock_command): self.setup.create_init_link_from_linuxrc() mock_command.assert_called_once_with( ['ln', 'root_dir/linuxrc', 'root_dir/init'] ) @patch('kiwi.command.Command.run') def test_create_recovery_archive_cleanup_only(self, mock_command): self.setup.oemconfig['recovery'] = False self.setup.create_recovery_archive() assert mock_command.call_args_list[0] == call( ['bash', '-c', 'rm -f root_dir/recovery.*'] ) @patch_open @patch('os.path.exists') @patch('kiwi.system.setup.Path.wipe') def test_create_fstab(self, mock_wipe, mock_exists, mock_open): mock_exists.return_value = True mock_open.return_value = self.context_manager_mock self.file_mock.read.return_value = 'append_entry' self.setup.create_fstab(['fstab_entry']) assert mock_open.call_args_list == [ call('root_dir/etc/fstab', 'w'), call('root_dir/etc/fstab.append', 'r') ] assert self.file_mock.write.call_args_list == [ call('fstab_entry\n'), call('append_entry') ] mock_wipe.assert_called_once_with('root_dir/etc/fstab.append') @patch('kiwi.command.Command.run') @patch('kiwi.system.setup.NamedTemporaryFile') @patch('kiwi.system.setup.ArchiveTar') @patch_open @patch('kiwi.system.setup.Compress') @patch('os.path.getsize') @patch('kiwi.system.setup.Path.wipe') def test_create_recovery_archive( self, mock_wipe, mock_getsize, mock_compress, mock_open, mock_archive, mock_temp, mock_command ): mock_open.return_value = self.context_manager_mock mock_getsize.return_value = 42 compress = mock.Mock() mock_compress.return_value = compress archive = mock.Mock() mock_archive.return_value = archive tmpdir = mock.Mock() tmpdir.name = 'tmpdir' mock_temp.return_value = tmpdir self.setup.oemconfig['recovery'] = True self.setup.oemconfig['recovery_inplace'] = True self.setup.create_recovery_archive() assert mock_command.call_args_list[0] == call( ['bash', '-c', 'rm -f root_dir/recovery.*'] ) mock_archive.assert_called_once_with( create_from_file_list=False, filename='tmpdir' ) archive.create.assert_called_once_with( exclude=['dev', 'proc', 'sys'], options=[ '--numeric-owner', '--hard-dereference', '--preserve-permissions' ], source_dir='root_dir' ) assert mock_command.call_args_list[1] == call( ['mv', 'tmpdir', 'root_dir/recovery.tar'] ) assert mock_open.call_args_list[0] == call( 'root_dir/recovery.tar.filesystem', 'w' ) assert self.file_mock.write.call_args_list[0] == call('ext3') assert mock_command.call_args_list[2] == call( ['bash', '-c', 'tar -tf root_dir/recovery.tar | wc -l'] ) assert mock_open.call_args_list[1] == call( 'root_dir/recovery.tar.files', 'w' ) assert mock_getsize.call_args_list[0] == call( 'root_dir/recovery.tar' ) assert self.file_mock.write.call_args_list[1] == call('1\n') assert mock_open.call_args_list[2] == call( 'root_dir/recovery.tar.size', 'w' ) assert self.file_mock.write.call_args_list[2] == call('42') mock_compress.assert_called_once_with( 'root_dir/recovery.tar' ) compress.gzip.assert_called_once_with() assert mock_getsize.call_args_list[1] == call( 'root_dir/recovery.tar.gz' ) assert mock_open.call_args_list[3] == call( 'root_dir/recovery.partition.size', 'w' ) assert self.file_mock.write.call_args_list[3] == call('300') mock_wipe.assert_called_once_with( 'root_dir/recovery.tar.gz' ) @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.Path.create') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_export_modprobe_setup( self, mock_exists, mock_DataSync, mock_path, mock_command ): data = mock.Mock() mock_DataSync.return_value = data mock_exists.return_value = True self.setup.export_modprobe_setup('target_root_dir') mock_path.assert_called_once_with('target_root_dir/etc') mock_DataSync.assert_called_once_with( 'root_dir/etc/modprobe.d', 'target_root_dir/etc/' ) data.sync_data.assert_called_once_with( options=['-z', '-a'] ) @patch('kiwi.system.setup.Command.run') @patch_open def test_export_package_list_rpm( self, mock_open, mock_command ): command = mock.Mock() command.output = 'packages_data' mock_command.return_value = command result = self.setup.export_package_list('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_has_calls([ call(['chroot', 'root_dir', 'rpm', '-E', '%_dbpath']), call([ 'rpm', '--root', 'root_dir', '-qa', '--qf', '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|%{DISTURL}\\n', '--dbpath', 'packages_data' ]) ]) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.packages', 'w' ) @patch('kiwi.system.setup.Command.run') @patch_open def test_export_package_list_rpm_no_dbpath( self, mock_open, mock_command ): cmd = mock.Mock() cmd.output = 'packages_data' def dbpath_check_fails(command): if '%_dbpath' in command: raise KiwiCommandError() else: return cmd mock_command.side_effect = dbpath_check_fails result = self.setup.export_package_list('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_has_calls([ call(['chroot', 'root_dir', 'rpm', '-E', '%_dbpath']), call([ 'rpm', '--root', 'root_dir', '-qa', '--qf', '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|%{DISTURL}\\n' ]) ]) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.packages', 'w' ) @patch('kiwi.system.setup.Command.run') @patch_open def test_export_package_list_dpkg( self, mock_open, mock_command ): command = mock.Mock() command.output = 'packages_data' mock_command.return_value = command self.xml_state.get_package_manager = mock.Mock( return_value='apt-get' ) result = self.setup.export_package_list('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_called_once_with([ 'dpkg-query', '--admindir', 'root_dir/var/lib/dpkg', '-W', '-f', '${Package}|None|${Version}|None|${Architecture}|None\\n' ]) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.packages', 'w' ) @patch('kiwi.system.setup.Command.run') @patch_open def test_export_package_verification( self, mock_open, mock_command ): command = mock.Mock() command.output = 'verification_data' mock_command.return_value = command result = self.setup.export_package_verification('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_has_calls([ call(['chroot', 'root_dir', 'rpm', '-E', '%_dbpath']), call(command=[ 'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'verification_data' ], raise_on_error=False) ]) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.verified', 'w' ) @patch('kiwi.system.setup.Command.run') @patch_open def test_export_package_verification_no_dbpath( self, mock_open, mock_command ): cmd = mock.Mock() cmd.output = 'verification_data' def dbpath_check_fails(command, raise_on_error): if '%_dbpath' in command: raise KiwiCommandError() else: return cmd mock_command.side_effect = dbpath_check_fails result = self.setup.export_package_verification('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_has_calls([ call(['chroot', 'root_dir', 'rpm', '-E', '%_dbpath']), call( command=['rpm', '--root', 'root_dir', '-Va'], raise_on_error=False ) ]) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.verified', 'w' ) @patch('kiwi.system.setup.Command.run') @patch_open def test_export_package_verification_dpkg( self, mock_open, mock_command ): command = mock.Mock() command.output = 'verification_data' mock_command.return_value = command self.xml_state.get_package_manager = mock.Mock( return_value='apt-get' ) result = self.setup.export_package_verification('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_called_once_with( command=[ 'dpkg', '--root', 'root_dir', '-V', '--verify-format', 'rpm' ], raise_on_error=False ) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.verified', 'w' ) @patch('kiwi.system.setup.Command.run') def test_set_selinux_file_contexts(self, mock_command): self.setup.set_selinux_file_contexts('security_context_file') mock_command.assert_called_once_with( [ 'chroot', 'root_dir', 'setfiles', 'security_context_file', '/', '-v' ] ) @patch('kiwi.system.setup.Repository') @patch('kiwi.system.setup.Uri') def test_import_repositories_marked_as_imageinclude( self, mock_uri, mock_repo ): uri = mock.Mock() mock_uri.return_value = uri uri.translate = mock.Mock( return_value="uri" ) uri.alias = mock.Mock( return_value="uri-alias" ) uri.credentials_file_name = mock.Mock( return_value='kiwiRepoCredentials' ) mock_uri.return_value = uri repo = mock.Mock() mock_repo.return_value = repo self.setup_with_real_xml.import_repositories_marked_as_imageinclude() assert repo.add_repo.call_args_list[0] == call( 'uri-alias', 'uri', 'rpm-md', None, None, None, None, None, 'kiwiRepoCredentials', None, None )
class TestSystemSetup: @fixture(autouse=True) def inject_fixtures(self, caplog): self._caplog = caplog @patch('kiwi.system.setup.RuntimeConfig') def setup(self, mock_RuntimeConfig): Defaults.set_platform_name('x86_64') self.runtime_config = Mock() self.runtime_config.get_package_changes = Mock(return_value=True) mock_RuntimeConfig.return_value = self.runtime_config self.xml_state = MagicMock() self.xml_state.get_package_manager = Mock(return_value='zypper') self.xml_state.build_type.get_filesystem = Mock(return_value='ext3') self.xml_state.xml_data.get_name = Mock(return_value='some-image') self.xml_state.get_image_version = Mock(return_value='1.2.3') self.xml_state.xml_data.description_dir = 'description_dir' self.setup = SystemSetup(self.xml_state, 'root_dir') description = XMLDescription(description='../data/example_config.xml', derived_from='derived/description') self.description_dir = os.path.dirname(description.description_origin) self.setup_with_real_xml = SystemSetup(XMLState(description.load()), 'root_dir') command_run = namedtuple('command', ['output', 'error', 'returncode']) self.run_result = command_run(output='password-hash\n', error='stderr', returncode=0) def teardown(self): sys.argv = argv_kiwi_tests def test_setup_ix86(self): Defaults.set_platform_name('i686') setup = SystemSetup(MagicMock(), 'root_dir') assert setup.arch == 'ix86' @patch('kiwi.command.Command.run') @patch('os.path.exists') @patch('kiwi.system.setup.glob.iglob') def test_import_description(self, mock_iglob, mock_path, mock_command): mock_iglob.return_value = ['config-cdroot.tar.xz'] mock_path.return_value = True with patch('builtins.open'): self.setup_with_real_xml.import_description() mock_iglob.assert_called_once_with('{0}/config-cdroot.tar*'.format( self.description_dir)) assert mock_command.call_args_list == [ call([ 'cp', '{0}/config.sh'.format(self.description_dir), 'root_dir/image/config.sh' ]), call([ 'cp', '{0}/disk.sh'.format(self.description_dir), 'root_dir/image/disk.sh' ]), call([ 'cp', '{0}/my_edit_boot_script'.format(self.description_dir), 'root_dir/image/edit_boot_config.sh' ]), call([ 'cp', '/absolute/path/to/my_edit_boot_install', 'root_dir/image/edit_boot_install.sh' ]), call([ 'cp', '{0}/images.sh'.format(self.description_dir), 'root_dir/image/images.sh' ]), call([ 'cp', '{0}/post_bootstrap.sh'.format(self.description_dir), 'root_dir/image/post_bootstrap.sh' ]), call([ 'cp', Defaults.project_file('config/functions.sh'), 'root_dir/.kconfig' ]), call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']), call([ 'cp', '{0}/bootstrap.tgz'.format(self.description_dir), 'root_dir/image/' ]), call(['cp', 'config-cdroot.tar.xz', 'root_dir/image/']) ] @patch('kiwi.path.Path.create') @patch('kiwi.command.Command.run') @patch('os.path.exists') def test_import_description_archive_from_derived(self, mock_path, mock_command, mock_create): path_return_values = [ True, False, True, True, True, True, True, True, True ] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect with patch('builtins.open'): self.setup_with_real_xml.import_description() assert mock_command.call_args_list == [ call([ 'cp', '{0}/config.sh'.format(self.description_dir), 'root_dir/image/config.sh' ]), call([ 'cp', '{0}/disk.sh'.format(self.description_dir), 'root_dir/image/disk.sh' ]), call([ 'cp', '{0}/my_edit_boot_script'.format(self.description_dir), 'root_dir/image/edit_boot_config.sh' ]), call([ 'cp', '/absolute/path/to/my_edit_boot_install', 'root_dir/image/edit_boot_install.sh' ]), call([ 'cp', '{0}/images.sh'.format(self.description_dir), 'root_dir/image/images.sh' ]), call([ 'cp', '{0}/post_bootstrap.sh'.format(self.description_dir), 'root_dir/image/post_bootstrap.sh' ]), call([ 'cp', Defaults.project_file('config/functions.sh'), 'root_dir/.kconfig' ]), call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']), call( ['cp', 'derived/description/bootstrap.tgz', 'root_dir/image/']) ] mock_create.assert_called_once_with('root_dir/image') @patch('kiwi.command.Command.run') @patch('os.path.exists') def test_import_description_configured_editboot_scripts_not_found( self, mock_path, mock_command): path_return_values = [False, True, True, True] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect with patch('builtins.open'): with raises(KiwiImportDescriptionError): self.setup_with_real_xml.import_description() @patch('kiwi.path.Path.create') @patch('kiwi.command.Command.run') @patch('os.path.exists') def test_import_description_configured_archives_not_found( self, mock_path, mock_command, mock_create): path_return_values = [False, False, True, True, True, True, True, True] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect with patch('builtins.open'): with raises(KiwiImportDescriptionError): self.setup_with_real_xml.import_description() @patch('kiwi.command.Command.run') def test_cleanup(self, mock_command): self.setup.cleanup() mock_command.assert_called_once_with( ['chroot', 'root_dir', 'rm', '-rf', '.kconfig', 'image']) @patch('kiwi.system.setup.ArchiveTar') @patch('kiwi.system.setup.glob.iglob') def test_import_cdroot_files(self, mock_iglob, mock_ArchiveTar): archive = Mock() mock_ArchiveTar.return_value = archive mock_iglob.return_value = ['config-cdroot.tar.xz'] self.setup.import_cdroot_files('target_dir') mock_iglob.assert_called_once_with( 'description_dir/config-cdroot.tar*') mock_ArchiveTar.assert_called_once_with('config-cdroot.tar.xz') archive.extract.assert_called_once_with('target_dir') @patch('kiwi.command.Command.run') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_import_overlay_files_copy_links(self, mock_os_path, mock_DataSync, mock_command): data = Mock() mock_DataSync.return_value = data mock_os_path.return_value = True self.setup.import_overlay_files(follow_links=True, preserve_owner_group=True) mock_DataSync.assert_called_once_with('description_dir/root/', 'root_dir') data.sync_data.assert_called_once_with(options=[ '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system', '--copy-links', '-o', '-g' ]) @patch('kiwi.command.Command.run') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_import_overlay_files_links(self, mock_os_path, mock_DataSync, mock_command): data = Mock() mock_DataSync.return_value = data mock_os_path.return_value = True self.setup.import_overlay_files(follow_links=False, preserve_owner_group=True) mock_DataSync.assert_called_once_with('description_dir/root/', 'root_dir') data.sync_data.assert_called_once_with(options=[ '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system', '--links', '-o', '-g' ]) @patch('kiwi.system.setup.ArchiveTar') @patch('os.path.exists') def test_import_overlay_files_from_archive(self, mock_os_path, mock_archive): archive = Mock() mock_archive.return_value = archive exists_results = [True, False] def side_effect(arg): return exists_results.pop() mock_os_path.side_effect = side_effect self.setup.import_overlay_files() mock_archive.assert_called_once_with('description_dir/root.tar.gz') archive.extract.assert_called_once_with('root_dir') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_import_overlay_files_from_profile(self, mock_os_path, mock_DataSync): exists_results = [True, False, False] def side_effect(arg): return exists_results.pop() data = Mock() mock_DataSync.return_value = data mock_os_path.side_effect = side_effect self.xml_state.profiles = ['profile_root'] self.setup.import_overlay_files() mock_DataSync.assert_called_once_with('description_dir/profile_root/', 'root_dir') data.sync_data.assert_called_once_with(options=[ '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system', '--links' ]) @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_keyboard_map(self, mock_path, mock_run, mock_shell): mock_path.return_value = True self.setup.preferences['keytable'] = 'keytable' self.setup.setup_keyboard_map() mock_shell.assert_called_once_with( 'baseUpdateSysConfig', ['root_dir/etc/sysconfig/keyboard', 'KEYTABLE', '"keytable"']) @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_keyboard_map_with_systemd(self, mock_path, mock_run, mock_shell, mock_caps): mock_caps.return_value = True mock_path.return_value = True self.setup.preferences['keytable'] = 'keytable' self.setup.setup_keyboard_map() mock_run.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/vconsole.conf']), call([ 'chroot', 'root_dir', 'systemd-firstboot', '--keymap=keytable' ]) ]) @patch('os.path.exists') def test_setup_keyboard_skipped(self, mock_exists): mock_exists.return_value = False self.setup.preferences['keytable'] = 'keytable' with self._caplog.at_level(logging.WARNING): self.setup.setup_keyboard_map() @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_locale(self, mock_path, mock_run, mock_caps): mock_caps.return_valure = True mock_path.return_value = True self.setup.preferences['locale'] = 'locale1,locale2' self.setup.setup_locale() mock_run.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/locale.conf']), call([ 'chroot', 'root_dir', 'systemd-firstboot', '--locale=locale1.UTF-8' ]) ]) @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_locale_POSIX(self, mock_path, mock_run, mock_caps): mock_caps.return_valure = True mock_path.return_value = True self.setup.preferences['locale'] = 'POSIX,locale2' self.setup.setup_locale() mock_run.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/locale.conf']), call(['chroot', 'root_dir', 'systemd-firstboot', '--locale=POSIX']) ]) @patch('kiwi.system.setup.Command.run') def test_setup_timezone(self, mock_command): self.setup.preferences['timezone'] = 'timezone' self.setup.setup_timezone() mock_command.assert_has_calls([ call([ 'chroot', 'root_dir', 'ln', '-s', '-f', '/usr/share/zoneinfo/timezone', '/etc/localtime' ]) ]) @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Command.run') def test_setup_timezone_with_systemd(self, mock_command, mock_caps): mock_caps.return_value = True self.setup.preferences['timezone'] = 'timezone' self.setup.setup_timezone() mock_command.assert_has_calls([ call([ 'chroot', 'root_dir', 'systemd-firstboot', '--timezone=timezone' ]) ]) @patch('kiwi.system.setup.Users') def test_setup_groups(self, mock_users): users = Mock() users.group_exists = Mock(return_value=False) mock_users.return_value = users self.setup_with_real_xml.setup_groups() calls = [call('users'), call('kiwi'), call('admin')] users.group_exists.assert_has_calls(calls) calls = [call('users', []), call('kiwi', []), call('admin', [])] users.group_add.assert_has_calls(calls) @patch('kiwi.system.setup.Users') @patch('kiwi.system.setup.Command.run') def test_setup_users_add(self, mock_command, mock_users): users = Mock() users.user_exists = Mock(return_value=False) mock_users.return_value = users mock_command.return_value = self.run_result self.setup_with_real_xml.setup_users() calls = [call('root'), call('tux'), call('kiwi')] users.user_exists.assert_has_calls(calls) calls = [ call('root', [ '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c', 'Bob', '-m', '-d', '/root' ]), call('tux', [ '-p', 'password-hash', '-g', 'users', '-m', '-d', '/home/tux' ]), call('kiwi', [ '-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users', '-m' ]) ] users.user_add.assert_has_calls(calls) mock_command.assert_called_with( ['openssl', 'passwd', '-1', '-salt', 'xyz', 'mypwd']) @patch('kiwi.system.setup.Users') @patch('kiwi.system.setup.Command.run') def test_setup_users_modify(self, mock_command, mock_users): users = Mock() users.user_exists = Mock(return_value=True) mock_users.return_value = users mock_command.return_value = self.run_result self.setup_with_real_xml.setup_users() calls = [call('root'), call('tux'), call('kiwi')] users.user_exists.assert_has_calls(calls) calls = [ call('root', [ '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c', 'Bob' ]), call('tux', ['-p', 'password-hash', '-g', 'users']), call('kiwi', ['-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users']) ] users.user_modify.assert_has_calls(calls) @patch('kiwi.system.setup.Path.which') @patch('kiwi.system.setup.Command.run') def test_setup_plymouth_splash(self, mock_command, mock_which): mock_which.return_value = 'plymouth-set-default-theme' preferences = Mock() preferences.get_bootsplash_theme = Mock(return_value=['some-theme']) self.xml_state.get_preferences_sections = Mock( return_value=[preferences]) self.setup.setup_plymouth_splash() mock_which.assert_called_once_with( root_dir='root_dir', filename='plymouth-set-default-theme') mock_command.assert_called_once_with( ['chroot', 'root_dir', 'plymouth-set-default-theme', 'some-theme']) @patch('os.path.exists') def test_import_image_identifier(self, mock_os_path): self.xml_state.xml_data.get_id = Mock(return_value='42') mock_os_path.return_value = True m_open = mock_open() with patch('builtins.open', m_open, create=True): self.setup.import_image_identifier() m_open.assert_called_once_with('root_dir/etc/ImageID', 'w') m_open.return_value.write.assert_called_once_with('42\n') @patch('kiwi.system.setup.Profile') @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') @patch('copy.deepcopy') def test_call_non_excutable_config_script(self, mock_copy_deepcopy, mock_access, mock_stat, mock_os_path, mock_watch, mock_command, mock_Profile): mock_copy_deepcopy.return_value = {} profile = Mock() mock_Profile.return_value = profile profile.get_settings.return_value = {} result_type = namedtuple('result', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result mock_access.return_value = False self.setup.call_config_script() mock_copy_deepcopy.assert_called_once_with(os.environ) mock_command.assert_called_once_with( ['chroot', 'root_dir', 'bash', 'image/config.sh'], {}) @patch('kiwi.system.setup.Profile') @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') @patch('copy.deepcopy') def test_call_excutable_config_script(self, mock_copy_deepcopy, mock_access, mock_stat, mock_os_path, mock_watch, mock_command, mock_Profile): mock_copy_deepcopy.return_value = {} profile = Mock() mock_Profile.return_value = profile profile.get_settings.return_value = {} result_type = namedtuple('result', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result # pretend that the script is executable mock_access.return_value = True self.setup.call_config_script() mock_copy_deepcopy.assert_called_once_with(os.environ) mock_command.assert_called_once_with( ['chroot', 'root_dir', 'image/config.sh'], {}) @patch('kiwi.system.setup.Profile') @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') @patch('copy.deepcopy') def test_call_excutable_post_bootstrap_script(self, mock_copy_deepcopy, mock_access, mock_stat, mock_os_path, mock_watch, mock_command, mock_Profile): mock_copy_deepcopy.return_value = {} profile = Mock() mock_Profile.return_value = profile profile.get_settings.return_value = {} result_type = namedtuple('result', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result # pretend that the script is executable mock_access.return_value = True self.setup.call_post_bootstrap_script() mock_copy_deepcopy.assert_called_once_with(os.environ) mock_command.assert_called_once_with( ['chroot', 'root_dir', 'image/post_bootstrap.sh'], {}) @patch('kiwi.system.setup.Profile') @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') @patch('copy.deepcopy') def test_call_disk_script(self, mock_copy_deepcopy, mock_access, mock_stat, mock_os_path, mock_watch, mock_command, mock_Profile): mock_copy_deepcopy.return_value = {} profile = Mock() mock_Profile.return_value = profile profile.get_settings.return_value = {} result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result mock_access.return_value = False self.setup.call_disk_script() mock_copy_deepcopy.assert_called_once_with(os.environ) mock_command.assert_called_once_with( ['chroot', 'root_dir', 'bash', 'image/disk.sh'], {}) @patch('kiwi.system.setup.Profile') @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') @patch('copy.deepcopy') def test_call_image_script(self, mock_copy_deepcopy, mock_access, mock_stat, mock_os_path, mock_watch, mock_command, mock_Profile): mock_copy_deepcopy.return_value = {} profile = Mock() mock_Profile.return_value = profile profile.get_settings.return_value = {} result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result mock_access.return_value = False self.setup.call_image_script() mock_copy_deepcopy.assert_called_once_with(os.environ) mock_command.assert_called_once_with( ['chroot', 'root_dir', 'bash', 'image/images.sh'], {}) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.path.abspath') def test_call_edit_boot_config_script(self, mock_abspath, mock_exists, mock_watch, mock_command): result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_exists.return_value = True mock_abspath.return_value = '/root_dir/image/edit_boot_config.sh' mock_watch.return_value = mock_result self.setup.call_edit_boot_config_script('ext4', 1) mock_abspath.assert_called_once_with( 'root_dir/image/edit_boot_config.sh') mock_command.assert_called_once_with([ 'bash', '-c', 'cd root_dir && bash --norc /root_dir/image/edit_boot_config.sh ' 'ext4 1' ]) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.path.abspath') def test_call_edit_boot_install_script(self, mock_abspath, mock_exists, mock_watch, mock_command): result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_exists.return_value = True mock_abspath.return_value = '/root_dir/image/edit_boot_install.sh' mock_watch.return_value = mock_result self.setup.call_edit_boot_install_script('my_image.raw', '/dev/mapper/loop0p1') mock_abspath.assert_called_once_with( 'root_dir/image/edit_boot_install.sh') mock_command.assert_called_once_with([ 'bash', '-c', 'cd root_dir && bash --norc /root_dir/image/edit_boot_install.sh ' 'my_image.raw /dev/mapper/loop0p1' ]) @patch('kiwi.system.setup.Profile') @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') def test_call_image_script_raises(self, mock_access, mock_stat, mock_os_path, mock_watch, mock_command, mock_Profile): profile = Mock() mock_Profile.return_value = profile profile.get_settings.return_value = {} result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=1) mock_os_path.return_value = True mock_watch.return_value = mock_result mock_access.return_value = False with raises(KiwiScriptFailed): self.setup.call_image_script() @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') def test_call_edit_boot_install_script_raises(self, mock_os_path, mock_watch, mock_command): result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=1) mock_os_path.return_value = True mock_watch.return_value = mock_result with raises(KiwiScriptFailed): self.setup.call_edit_boot_install_script('my_image.raw', '/dev/mapper/loop0p1') @patch('kiwi.command.Command.run') def test_create_init_link_from_linuxrc(self, mock_command): self.setup.create_init_link_from_linuxrc() mock_command.assert_called_once_with( ['ln', 'root_dir/linuxrc', 'root_dir/init']) @patch('kiwi.command.Command.run') def test_create_recovery_archive_cleanup_only(self, mock_command): self.setup.oemconfig['recovery'] = False self.setup.create_recovery_archive() assert mock_command.call_args_list[0] == call( ['bash', '-c', 'rm -f root_dir/recovery.*']) @patch('os.path.exists') @patch('kiwi.system.setup.Path.wipe') @patch('kiwi.command.Command.run') def test_create_fstab(self, mock_command, mock_wipe, mock_exists): fstab = Mock() mock_exists.return_value = True m_open = mock_open(read_data='append_entry') with patch('builtins.open', m_open, create=True): self.setup.create_fstab(fstab) fstab.export.assert_called_once_with('root_dir/etc/fstab') assert m_open.call_args_list == [ call('root_dir/etc/fstab', 'a'), call('root_dir/etc/fstab.append', 'r') ] assert m_open.return_value.write.call_args_list == [ call('append_entry') ] assert mock_command.call_args_list == [ call(['patch', 'root_dir/etc/fstab', 'root_dir/etc/fstab.patch']), call(['chroot', 'root_dir', '/etc/fstab.script']) ] assert mock_wipe.call_args_list == [ call('root_dir/etc/fstab.append'), call('root_dir/etc/fstab.patch'), call('root_dir/etc/fstab.script') ] @patch('kiwi.command.Command.run') @patch('pathlib.Path.touch') @patch('kiwi.system.setup.ArchiveTar') @patch('kiwi.system.setup.Compress') @patch('os.path.getsize') @patch('kiwi.system.setup.Path.wipe') def test_create_recovery_archive(self, mock_wipe, mock_getsize, mock_compress, mock_archive, mock_pathlib_Path_touch, mock_command): mock_getsize.return_value = 42 compress = Mock() mock_compress.return_value = compress archive = Mock() mock_archive.return_value = archive self.setup.oemconfig['recovery'] = True self.setup.oemconfig['recovery_inplace'] = True m_open = mock_open() with patch('builtins.open', m_open, create=True): self.setup.create_recovery_archive() assert mock_command.call_args_list[0] == call( ['bash', '-c', 'rm -f root_dir/recovery.*']) mock_archive.assert_called_once_with(create_from_file_list=False, filename='root_dir/recovery.tar') archive.create.assert_called_once_with(exclude=['dev', 'proc', 'sys'], options=[ '--numeric-owner', '--hard-dereference', '--preserve-permissions' ], source_dir='root_dir') assert m_open.call_args_list[0] == call( 'root_dir/recovery.tar.filesystem', 'w') assert m_open.return_value.write.call_args_list[0] == call('ext3') assert mock_command.call_args_list[1] == call( ['bash', '-c', 'tar -tf root_dir/recovery.tar | wc -l']) assert m_open.call_args_list[1] == call('root_dir/recovery.tar.files', 'w') assert mock_getsize.call_args_list[0] == call('root_dir/recovery.tar') assert m_open.return_value.write.call_args_list[1] == call('1\n') assert m_open.call_args_list[2] == call('root_dir/recovery.tar.size', 'w') assert m_open.return_value.write.call_args_list[2] == call('42') mock_compress.assert_called_once_with('root_dir/recovery.tar') compress.gzip.assert_called_once_with() assert mock_getsize.call_args_list[1] == call( 'root_dir/recovery.tar.gz') assert m_open.call_args_list[3] == call( 'root_dir/recovery.partition.size', 'w') assert m_open.return_value.write.call_args_list[3] == call('300') mock_wipe.assert_called_once_with('root_dir/recovery.tar.gz') @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.Path.create') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_export_modprobe_setup(self, mock_exists, mock_DataSync, mock_path, mock_command): data = Mock() mock_DataSync.return_value = data mock_exists.return_value = True self.setup.export_modprobe_setup('target_root_dir') mock_path.assert_called_once_with('target_root_dir/etc') mock_DataSync.assert_called_once_with('root_dir/etc/modprobe.d', 'target_root_dir/etc/') data.sync_data.assert_called_once_with(options=['-a']) @patch('kiwi.defaults.Defaults.get_default_packager_tool') def test_export_package_list_unknown_packager( self, mock_get_default_packager_tool): assert self.setup.export_package_list('target_dir') == '' @patch('kiwi.defaults.Defaults.get_default_packager_tool') def test_export_package_changes_unknown_packager( self, mock_get_default_packager_tool): assert self.setup.export_package_changes('target_dir') == '' @patch('kiwi.defaults.Defaults.get_default_packager_tool') def test_export_package_verification_unknown_packager( self, mock_get_default_packager_tool): assert self.setup.export_package_verification('target_dir') == '' @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.RpmDataBase') @patch('kiwi.system.setup.MountManager') def test_export_package_list_rpm(self, mock_MountManager, mock_RpmDataBase, mock_command): rpmdb = Mock() rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath' rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath' rpmdb.has_rpm.return_value = True mock_RpmDataBase.return_value = rpmdb command = Mock() command.output = 'packages_data' mock_command.return_value = command with patch('builtins.open') as m_open: result = self.setup.export_package_list('target_dir') m_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.packages', 'w', encoding='utf-8') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_called_once_with([ 'rpm', '--root', 'root_dir', '-qa', '--qf', '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|' '%{DISTURL}|%{LICENSE}\\n', '--dbpath', 'image_dbpath' ]) rpmdb.has_rpm.return_value = False mock_command.reset_mock() with patch('builtins.open'): result = self.setup.export_package_list('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_called_once_with([ 'rpm', '--root', 'root_dir', '-qa', '--qf', '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|' '%{DISTURL}|%{LICENSE}\\n', '--dbpath', 'host_dbpath' ]) @patch('kiwi.system.setup.os.path.exists') def test_setup_machine_id(self, mock_path_exists): mock_path_exists.return_value = True with patch('builtins.open') as m_open: self.setup.setup_machine_id() m_open.assert_called_once_with('root_dir/etc/machine-id', 'w') mock_path_exists.return_value = False self.setup.setup_machine_id() @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.Path.which') def test_setup_permissions(self, mock_path_which, mock_command): mock_path_which.return_value = 'chkstat' self.setup.setup_permissions() mock_command.assert_called_once_with( ['chroot', 'root_dir', 'chkstat', '--system', '--set']) mock_path_which.return_value = None with self._caplog.at_level(logging.WARNING): self.setup.setup_permissions() @patch('kiwi.system.setup.Command.run') def test_export_package_list_dpkg(self, mock_command): command = Mock() command.output = 'packages_data' mock_command.return_value = command self.xml_state.get_package_manager = Mock(return_value='apt') with patch('builtins.open') as m_open: result = self.setup.export_package_list('target_dir') m_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.packages', 'w', encoding='utf-8') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_called_once_with([ 'dpkg-query', '--admindir', 'root_dir/var/lib/dpkg', '-W', '-f', '${Package}|None|${Version}|None|${Architecture}|None|None\\n' ]) @patch('kiwi.system.setup.Command.run') def test_export_package_list_pacman(self, mock_command): command = Mock() command.output = 'packages_data' mock_command.return_value = command self.xml_state.get_package_manager = Mock(return_value='pacman') with patch('builtins.open') as m_open: result = self.setup.export_package_list('target_dir') m_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.packages', 'w') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_called_once_with( ['pacman', '--query', '--dbpath', 'root_dir/var/lib/pacman']) @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.RpmDataBase') @patch('kiwi.system.setup.MountManager') def test_export_package_changes_rpm(self, mock_MountManager, mock_RpmDataBase, mock_command): rpmdb = Mock() rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath' rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath' rpmdb.has_rpm.return_value = True mock_RpmDataBase.return_value = rpmdb command = Mock() command.output = 'package|\nchanges' mock_command.return_value = command with patch('builtins.open', create=True) as mock_open: mock_open.return_value = MagicMock(spec=io.IOBase) result = self.setup.export_package_changes('target_dir') mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.changes', 'w', encoding='utf-8') assert result == 'target_dir/some-image.x86_64-1.2.3.changes' mock_command.assert_called_once_with([ 'rpm', '--root', 'root_dir', '-qa', '--qf', '%{NAME}|\\n', '--changelog', '--dbpath', 'image_dbpath' ]) self.runtime_config.get_package_changes.assert_called_once_with() @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') @patch('os.listdir') def test_export_package_changes_dpkg(self, mock_os_listdir, mock_os_path_exists, mock_command): command = Mock() command.output = 'changes' mock_command.return_value = command self.xml_state.get_package_manager = Mock(return_value='apt') mock_os_listdir.return_value = ['package_b', 'package_a'] mock_os_path_exists.return_value = True with patch('builtins.open', create=True) as mock_open: mock_open.return_value = MagicMock(spec=io.IOBase) result = self.setup.export_package_changes('target_dir') file_handle = mock_open.return_value.__enter__.return_value mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.changes', 'w', encoding='utf-8') assert result == 'target_dir/some-image.x86_64-1.2.3.changes' assert file_handle.write.call_args_list == [ call('package_a|\n'), call('changes\n'), call('package_b|\n'), call('changes\n') ] self.runtime_config.get_package_changes.assert_called_once_with() @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.RpmDataBase') @patch('kiwi.system.setup.MountManager') def test_export_package_verification(self, mock_MountManager, mock_RpmDataBase, mock_command): is_mounted_return = [True, False] def is_mounted(): return is_mounted_return.pop() shared_mount = Mock() shared_mount.is_mounted.side_effect = is_mounted mock_MountManager.return_value = shared_mount rpmdb = Mock() rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath' rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath' rpmdb.has_rpm.return_value = True mock_RpmDataBase.return_value = rpmdb command = Mock() command.output = 'verification_data' mock_command.return_value = command with patch('builtins.open') as m_open: result = self.setup.export_package_verification('target_dir') m_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.verified', 'w', encoding='utf-8') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_called_once_with(command=[ 'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'image_dbpath' ], raise_on_error=False) mock_MountManager.assert_called_once_with(device='/dev', mountpoint='root_dir/dev') shared_mount.bind_mount.assert_called_once_with() shared_mount.umount_lazy.assert_called_once_with() rpmdb.has_rpm.return_value = False is_mounted_return = [True, False] mock_command.reset_mock() shared_mount.reset_mock() with patch('builtins.open'): result = self.setup.export_package_verification('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_called_once_with(command=[ 'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'host_dbpath' ], raise_on_error=False) shared_mount.bind_mount.assert_called_once_with() shared_mount.umount_lazy.assert_called_once_with() @patch('kiwi.system.setup.Command.run') def test_export_package_verification_dpkg(self, mock_command): command = Mock() command.output = 'verification_data' mock_command.return_value = command self.xml_state.get_package_manager = Mock(return_value='apt') with patch('builtins.open') as m_open: result = self.setup.export_package_verification('target_dir') m_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.verified', 'w', encoding='utf-8') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_called_once_with(command=[ 'dpkg', '--root', 'root_dir', '-V', '--verify-format', 'rpm' ], raise_on_error=False) @patch('kiwi.system.setup.Command.run') def test_set_selinux_file_contexts(self, mock_command): self.setup.set_selinux_file_contexts('security_context_file') mock_command.assert_called_once_with([ 'chroot', 'root_dir', 'setfiles', 'security_context_file', '/', '-v' ]) @patch('kiwi.system.setup.Repository.new') @patch('kiwi.system.setup.Uri') def test_import_repositories_marked_as_imageinclude( self, mock_uri, mock_repo): uri = Mock() mock_uri.return_value = uri uri.translate = Mock(return_value="uri") uri.alias = Mock(return_value="uri-alias") uri.credentials_file_name = Mock(return_value='kiwiRepoCredentials') mock_uri.return_value = uri repo = Mock() mock_repo.return_value = repo self.setup_with_real_xml.import_repositories_marked_as_imageinclude() assert repo.add_repo.call_args_list[0] == call( 'uri-alias', 'uri', 'rpm-md', None, None, None, None, None, 'kiwiRepoCredentials', None, None, None, False, '../data/script') @patch('os.path.exists') def test_script_exists(self, mock_path_exists): assert self.setup.script_exists('some-script') == \ mock_path_exists.return_value
class InstallImageBuilder: """ **Installation image builder** :param object xml_state: instance of :class:`XMLState` :param str root_dir: system image root directory :param str target_dir: target directory path name :param object boot_image_task: instance of :class:`BootImage` :param dict custom_args: Custom processing arguments defined as hash keys: * xz_options: string of XZ compression parameters """ def __init__(self, xml_state: XMLState, root_dir: str, target_dir: str, boot_image_task: Optional[BootImageBase], custom_args: Dict = None) -> None: self.arch = Defaults.get_platform_name() self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.root_filesystem_is_multipath = \ xml_state.get_oemconfig_oem_multipath_scan() self.initrd_system = xml_state.get_initrd_system() self.firmware = FirmWare(xml_state) self.setup = SystemSetup(self.xml_state, self.root_dir) self.iso_volume_id = self.xml_state.build_type.get_volid() or \ Defaults.get_install_volume_id() self.diskname = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version(), '.raw' ]) self.isoname = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version(), '.install.iso' ]) self.pxename = ''.join([ xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version() ]) self.pxetarball = ''.join( [target_dir, '/', self.pxename, '.install.tar']) self.dracut_config_file = ''.join( [self.root_dir, Defaults.get_dracut_conf_name()]) self.squashed_diskname = ''.join( [xml_state.xml_data.get_name(), '.raw']) self.md5name = ''.join([xml_state.xml_data.get_name(), '.md5']) self.xz_options = custom_args['xz_options'] if custom_args \ and 'xz_options' in custom_args else None self.mbrid = SystemIdentifier() self.mbrid.calculate_id() self.custom_iso_args: Dict = {} if not boot_image_task: self.boot_image_task = BootImage.new(xml_state, target_dir, root_dir) self.boot_image_task.prepare() else: self.boot_image_task = boot_image_task def create_install_iso(self) -> None: """ Create an install ISO from the disk_image as hybrid ISO bootable via legacy BIOS, EFI and as disk from Stick Image types which triggers this builder are: * installiso="true|false" * installstick="true|false" """ self.media_dir = Temporary(prefix='kiwi_install_media.', path=self.target_dir).new_dir() # unpack cdroot user files to media dir self.setup.import_cdroot_files(self.media_dir.name) # custom iso metadata self.custom_iso_args = { 'meta_data': { 'volume_id': self.iso_volume_id, 'mbr_id': self.mbrid.get_id(), 'efi_mode': self.firmware.efi_mode(), 'ofw_mode': self.firmware.ofw_mode() } } # the system image transfer is checked against a checksum log.info('Creating disk image checksum') self.squashed_contents = Temporary(prefix='kiwi_install_squashfs.', path=self.target_dir).new_dir() checksum = Checksum(self.diskname) checksum.md5(self.squashed_contents.name + '/' + self.md5name) # the system image name is stored in a config file self._write_install_image_info_to_iso_image() if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the system image is stored as squashfs embedded file log.info('Creating squashfs embedded disk image') Command.run([ 'cp', '-l', self.diskname, self.squashed_contents.name + '/' + self.squashed_diskname ]) squashed_image_file = ''.join( [self.target_dir, '/', self.squashed_diskname, '.squashfs']) squashed_image = FileSystemSquashFs( device_provider=DeviceProvider(), root_dir=self.squashed_contents.name, custom_args={ 'compression': self.xml_state.build_type.get_squashfscompression() }) squashed_image.create_on_file(squashed_image_file) Command.run(['mv', squashed_image_file, self.media_dir.name]) log.info('Setting up install image bootloader configuration') if self.firmware.efi_mode(): # setup bootloader config to boot the ISO via EFI # This also embedds an MBR and the respective BIOS modules # for compat boot. The complete bootloader setup will be # based on grub bootloader_config = BootLoaderConfig.new( 'grub2', self.xml_state, root_dir=self.root_dir, boot_dir=self.media_dir.name, custom_args={ 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) }) bootloader_config.setup_install_boot_images( mbrid=self.mbrid, lookup_path=self.boot_image_task.boot_root_directory) else: # setup bootloader config to boot the ISO via isolinux. # This allows for booting on x86 platforms in BIOS mode # only. bootloader_config = BootLoaderConfig.new( 'isolinux', self.xml_state, root_dir=self.root_dir, boot_dir=self.media_dir.name) IsoToolsBase.setup_media_loader_directory( self.boot_image_task.boot_root_directory, self.media_dir.name, bootloader_config.get_boot_theme()) bootloader_config.write_meta_data() bootloader_config.setup_install_image_config(mbrid=self.mbrid) bootloader_config.write() if self.firmware.efi_mode(): efi_loader = Temporary(prefix='efi-loader.', path=self.target_dir).new_file() bootloader_config._create_embedded_fat_efi_image(efi_loader.name) self.custom_iso_args['meta_data']['efi_loader'] = efi_loader.name # create initrd for install image log.info('Creating install image boot image') self._create_iso_install_kernel_and_initrd() # the system image initrd is stored to allow kexec self._copy_system_image_initrd_to_iso_image() # create iso filesystem from media_dir log.info('Creating ISO filesystem') iso_image = FileSystemIsoFs(device_provider=DeviceProvider(), root_dir=self.media_dir.name, custom_args=self.custom_iso_args) iso_image.create_on_file(self.isoname) self.boot_image_task.cleanup() def create_install_pxe_archive(self) -> None: """ Create an oem install tar archive suitable for installing a disk image via the network using the PXE boot protocol. The archive contains: * The raw system image xz compressed * The raw system image checksum metadata file * The append file template for the boot server * The system image initrd for kexec * The install initrd * The kernel Image types which triggers this builder are: * installpxe="true|false" """ self.pxe_dir = Temporary(prefix='kiwi_pxe_install_media.', path=self.target_dir).new_dir() # the system image is transfered as xz compressed variant log.info('xz compressing disk image') pxe_image_filename = ''.join( [self.pxe_dir.name, '/', self.pxename, '.xz']) compress = Compress(source_filename=self.diskname, keep_source_on_compress=True) compress.xz(self.xz_options) Command.run(['mv', compress.compressed_filename, pxe_image_filename]) # the system image transfer is checked against a checksum log.info('Creating disk image checksum') pxe_md5_filename = ''.join( [self.pxe_dir.name, '/', self.pxename, '.md5']) checksum = Checksum(self.diskname) checksum.md5(pxe_md5_filename) # the install image name is stored in a config file if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the kexec required system image initrd is stored for dracut kiwi-dump if self.initrd_system == 'dracut': boot_names = self.boot_image_task.get_boot_names() system_image_initrd = os.sep.join( [self.root_dir, 'boot', boot_names.initrd_name]) target_initrd_name = '{0}/{1}.initrd'.format( self.pxe_dir.name, self.pxename) shutil.copy(system_image_initrd, target_initrd_name) os.chmod(target_initrd_name, 420) # create pxe config append information # this information helps to configure the boot server correctly append_filename = ''.join( [self.pxe_dir.name, '/', self.pxename, '.append']) if self.initrd_system == 'kiwi': cmdline = 'pxe=1' else: cmdline = ' '.join([ 'rd.kiwi.install.pxe', 'rd.kiwi.install.image=http://example.com/image.xz' ]) custom_cmdline = self.xml_state.build_type.get_kernelcmdline() if custom_cmdline: cmdline += ' ' + custom_cmdline with open(append_filename, 'w') as append: append.write('%s\n' % cmdline) # create initrd for pxe install log.info('Creating pxe install boot image') self._create_pxe_install_kernel_and_initrd() # create pxe image bound boot config file, contents can be # changed but presence is required. log.info('Creating pxe install boot options file') configname = '{0}.config.bootoptions'.format(self.pxename) shutil.copy(os.sep.join([self.root_dir, 'config.bootoptions']), os.sep.join([self.pxe_dir.name, configname])) # create pxe install tarball log.info('Creating pxe install archive') archive = ArchiveTar(self.pxetarball) archive.create(self.pxe_dir.name) self.boot_image_task.cleanup() def _create_pxe_install_kernel_and_initrd(self) -> None: kernelname = 'pxeboot.{0}.kernel'.format(self.pxename) initrdname = 'pxeboot.{0}.initrd'.format(self.pxename) kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(self.pxe_dir.name, kernelname) os.symlink( kernelname, ''.join([self.pxe_dir.name, '/', self.pxename, '.kernel'])) else: raise KiwiInstallBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor( self.pxe_dir.name, '/pxeboot.{0}.xen.gz'.format(self.pxename)) else: raise KiwiInstallBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory) if self.initrd_system == 'dracut': self.boot_image_task.include_module('kiwi-dump') self.boot_image_task.include_module('kiwi-dump-reboot') if self.root_filesystem_is_multipath is False: self.boot_image_task.omit_module('multipath') for mod in self.xml_state.get_installmedia_initrd_modules('add'): self.boot_image_task.include_module(mod) for mod in self.xml_state.get_installmedia_initrd_modules('omit'): self.boot_image_task.omit_module(mod) self.boot_image_task.set_static_modules( self.xml_state.get_installmedia_initrd_modules('set')) self.boot_image_task.create_initrd(self.mbrid, 'initrd_kiwi_install', install_initrd=True) Command.run([ 'mv', self.boot_image_task.initrd_filename, self.pxe_dir.name + '/{0}'.format(initrdname) ]) os.chmod(self.pxe_dir.name + '/{0}'.format(initrdname), 420) def _create_iso_install_kernel_and_initrd(self) -> None: boot_path = self.media_dir.name + '/boot/' + self.arch + '/loader' Path.create(boot_path) kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(boot_path, '/linux') else: raise KiwiInstallBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor(boot_path, '/xen.gz') else: raise KiwiInstallBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory) if self.initrd_system == 'dracut': self.boot_image_task.include_module('kiwi-dump') self.boot_image_task.include_module('kiwi-dump-reboot') if self.root_filesystem_is_multipath is False: self.boot_image_task.omit_module('multipath') for mod in self.xml_state.get_installmedia_initrd_modules('add'): self.boot_image_task.include_module(mod) for mod in self.xml_state.get_installmedia_initrd_modules('omit'): self.boot_image_task.omit_module(mod) self.boot_image_task.set_static_modules( self.xml_state.get_installmedia_initrd_modules('set')) self._add_system_image_boot_options_to_boot_image() self.boot_image_task.create_initrd(self.mbrid, 'initrd_kiwi_install', install_initrd=True) Command.run([ 'mv', self.boot_image_task.initrd_filename, boot_path + '/initrd' ]) def _add_system_image_boot_options_to_boot_image(self) -> None: filename = ''.join( [self.boot_image_task.boot_root_directory, '/config.bootoptions']) self.boot_image_task.include_file(os.sep + os.path.basename(filename)) def _copy_system_image_initrd_to_iso_image(self) -> None: boot_names = self.boot_image_task.get_boot_names() system_image_initrd = os.sep.join( [self.root_dir, 'boot', boot_names.initrd_name]) shutil.copy(system_image_initrd, self.media_dir.name + '/initrd.system_image') def _write_install_image_info_to_iso_image(self) -> None: iso_trigger = self.media_dir.name + '/config.isoclient' with open(iso_trigger, 'w') as iso_system: iso_system.write('IMAGE="%s"\n' % self.squashed_diskname) def _write_install_image_info_to_boot_image(self) -> None: initrd_trigger = \ self.boot_image_task.boot_root_directory + '/config.vmxsystem' with open(initrd_trigger, 'w') as vmx_system: vmx_system.write('IMAGE="%s"\n' % self.squashed_diskname)
class InstallImageBuilder(object): """ **Installation image builder** :param object xml_state: instance of :class:`XMLState` :param str root_dir: system image root directory :param str target_dir: target directory path name :param object boot_image_task: instance of :class:`BootImage` :param dict custom_args: Custom processing arguments defined as hash keys: * xz_options: string of XZ compression parameters """ def __init__(self, xml_state, root_dir, target_dir, boot_image_task, custom_args=None): self.arch = platform.machine() if self.arch == 'i686' or self.arch == 'i586': self.arch = 'ix86' self.root_dir = root_dir self.target_dir = target_dir self.boot_image_task = boot_image_task self.xml_state = xml_state self.root_filesystem_is_multipath = \ xml_state.get_oemconfig_oem_multipath_scan() self.initrd_system = xml_state.get_initrd_system() self.firmware = FirmWare(xml_state) self.setup = SystemSetup(self.xml_state, self.root_dir) self.iso_volume_id = self.xml_state.build_type.get_volid() or \ Defaults.get_install_volume_id() self.diskname = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version(), '.raw' ]) self.isoname = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version(), '.install.iso' ]) self.pxename = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version(), '.install.tar.xz' ]) self.dracut_config_file = ''.join( [self.root_dir, Defaults.get_dracut_conf_name()]) self.squashed_diskname = ''.join( [xml_state.xml_data.get_name(), '.raw']) self.md5name = ''.join([xml_state.xml_data.get_name(), '.md5']) self.xz_options = custom_args['xz_options'] if custom_args \ and 'xz_options' in custom_args else None self.mbrid = SystemIdentifier() self.mbrid.calculate_id() self.media_dir = None self.pxe_dir = None self.squashed_contents = None self.custom_iso_args = None def create_install_iso(self): """ Create an install ISO from the disk_image as hybrid ISO bootable via legacy BIOS, EFI and as disk from Stick Image types which triggers this builder are: * installiso="true|false" * installstick="true|false" """ self.media_dir = mkdtemp(prefix='kiwi_install_media.', dir=self.target_dir) # unpack cdroot user files to media dir self.setup.import_cdroot_files(self.media_dir) # custom iso metadata self.custom_iso_args = { 'meta_data': { 'volume_id': self.iso_volume_id, 'mbr_id': self.mbrid.get_id(), 'efi_mode': self.firmware.efi_mode() } } # the system image transfer is checked against a checksum log.info('Creating disk image checksum') self.squashed_contents = mkdtemp(prefix='kiwi_install_squashfs.', dir=self.target_dir) checksum = Checksum(self.diskname) checksum.md5(self.squashed_contents + '/' + self.md5name) # the system image name is stored in a config file self._write_install_image_info_to_iso_image() if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the system image is stored as squashfs embedded file log.info('Creating squashfs embedded disk image') Command.run([ 'cp', '-l', self.diskname, self.squashed_contents + '/' + self.squashed_diskname ]) squashed_image_file = ''.join( [self.target_dir, '/', self.squashed_diskname, '.squashfs']) squashed_image = FileSystemSquashFs(device_provider=None, root_dir=self.squashed_contents) squashed_image.create_on_file(squashed_image_file) Command.run(['mv', squashed_image_file, self.media_dir]) # setup bootloader config to boot the ISO via isolinux log.info('Setting up install image bootloader configuration') bootloader_config_isolinux = BootLoaderConfig('isolinux', self.xml_state, self.media_dir) bootloader_config_isolinux.setup_install_boot_images( mbrid=None, lookup_path=self.boot_image_task.boot_root_directory) bootloader_config_isolinux.setup_install_image_config(mbrid=None) bootloader_config_isolinux.write() # setup bootloader config to boot the ISO via EFI bootloader_config_grub = BootLoaderConfig( 'grub2', self.xml_state, self.media_dir, { 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) }) bootloader_config_grub.setup_install_boot_images( mbrid=self.mbrid, lookup_path=self.root_dir) bootloader_config_grub.setup_install_image_config(mbrid=self.mbrid) bootloader_config_grub.write() # create initrd for install image log.info('Creating install image boot image') self._create_iso_install_kernel_and_initrd() # the system image initrd is stored to allow kexec self._copy_system_image_initrd_to_iso_image() # create iso filesystem from media_dir log.info('Creating ISO filesystem') iso_image = FileSystemIsoFs(device_provider=None, root_dir=self.media_dir, custom_args=self.custom_iso_args) iso_image.create_on_file(self.isoname) def create_install_pxe_archive(self): """ Create an oem install tar archive suitable for installing a disk image via the network using the PXE boot protocol. The archive contains: * The raw system image xz compressed * The raw system image checksum metadata file * The append file template for the boot server * The system image initrd for kexec * The install initrd * The kernel Image types which triggers this builder are: * installpxe="true|false" """ self.pxe_dir = mkdtemp(prefix='kiwi_pxe_install_media.', dir=self.target_dir) # the system image is transfered as xz compressed variant log.info('xz compressing disk image') pxe_image_filename = ''.join( [self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.xz']) compress = Compress(source_filename=self.diskname, keep_source_on_compress=True) compress.xz(self.xz_options) Command.run(['mv', compress.compressed_filename, pxe_image_filename]) # the system image transfer is checked against a checksum log.info('Creating disk image checksum') pxe_md5_filename = ''.join( [self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.md5']) checksum = Checksum(self.diskname) checksum.md5(pxe_md5_filename) # the install image name is stored in a config file if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the kexec required system image initrd is stored for dracut kiwi-dump if self.initrd_system == 'dracut': boot_names = self.boot_image_task.get_boot_names() system_image_initrd = os.sep.join( [self.root_dir, 'boot', boot_names.initrd_name]) target_initrd_name = '{0}/{1}.initrd'.format( self.pxe_dir, self.xml_state.xml_data.get_name()) shutil.copy(system_image_initrd, target_initrd_name) os.chmod(target_initrd_name, 420) # create pxe config append information # this information helps to configure the boot server correctly append_filename = ''.join( [self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.append']) if self.initrd_system == 'kiwi': cmdline = 'pxe=1' else: cmdline = ' '.join([ 'rd.kiwi.install.pxe', 'rd.kiwi.install.image=http://example.com/image.xz' ]) custom_cmdline = self.xml_state.build_type.get_kernelcmdline() if custom_cmdline: cmdline += ' ' + custom_cmdline with open(append_filename, 'w') as append: append.write('%s\n' % cmdline) # create initrd for pxe install log.info('Creating pxe install boot image') self._create_pxe_install_kernel_and_initrd() # create pxe install tarball log.info('Creating pxe install archive') archive = ArchiveTar(self.pxename.replace('.xz', '')) archive.create_xz_compressed(self.pxe_dir, xz_options=self.xz_options) def _create_pxe_install_kernel_and_initrd(self): kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(self.pxe_dir, '/pxeboot.kernel') os.symlink( 'pxeboot.kernel', ''.join([ self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.kernel' ])) else: raise KiwiInstallBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor(self.pxe_dir, '/pxeboot.xen.gz') else: raise KiwiInstallBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory) if self.initrd_system == 'dracut': self._create_dracut_install_config() self._add_system_image_boot_options_to_boot_image() self.boot_image_task.create_initrd(self.mbrid, 'initrd_kiwi_install') Command.run([ 'mv', self.boot_image_task.initrd_filename, self.pxe_dir + '/pxeboot.initrd.xz' ]) os.chmod(self.pxe_dir + '/pxeboot.initrd.xz', 420) def _create_iso_install_kernel_and_initrd(self): boot_path = self.media_dir + '/boot/' + self.arch + '/loader' Path.create(boot_path) kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(boot_path, '/linux') else: raise KiwiInstallBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor(boot_path, '/xen.gz') else: raise KiwiInstallBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory) if self.initrd_system == 'dracut': self._create_dracut_install_config() self._add_system_image_boot_options_to_boot_image() self.boot_image_task.create_initrd(self.mbrid, 'initrd_kiwi_install') Command.run([ 'mv', self.boot_image_task.initrd_filename, boot_path + '/initrd' ]) def _add_system_image_boot_options_to_boot_image(self): filename = ''.join( [self.boot_image_task.boot_root_directory, '/config.bootoptions']) self.boot_image_task.include_file(os.sep + os.path.basename(filename)) def _copy_system_image_initrd_to_iso_image(self): boot_names = self.boot_image_task.get_boot_names() system_image_initrd = os.sep.join( [self.root_dir, 'boot', boot_names.initrd_name]) shutil.copy(system_image_initrd, self.media_dir + '/initrd.system_image') def _write_install_image_info_to_iso_image(self): iso_trigger = self.media_dir + '/config.isoclient' with open(iso_trigger, 'w') as iso_system: iso_system.write('IMAGE="%s"\n' % self.squashed_diskname) def _write_install_image_info_to_boot_image(self): initrd_trigger = \ self.boot_image_task.boot_root_directory + '/config.vmxsystem' with open(initrd_trigger, 'w') as vmx_system: vmx_system.write('IMAGE="%s"\n' % self.squashed_diskname) def _create_dracut_install_config(self): dracut_config = ['hostonly="no"', 'dracut_rescue_image="no"'] dracut_modules = ['kiwi-lib', 'kiwi-dump'] dracut_modules_omit = ['kiwi-overlay', 'kiwi-live', 'kiwi-repart'] if self.root_filesystem_is_multipath is False: dracut_modules_omit.append('multipath') dracut_config.append('add_dracutmodules+=" {0} "'.format( ' '.join(dracut_modules))) dracut_config.append('omit_dracutmodules+=" {0} "'.format( ' '.join(dracut_modules_omit))) with open(self.dracut_config_file, 'w') as config: for entry in dracut_config: config.write(entry + os.linesep) def _delete_dracut_install_config(self): if os.path.exists(self.dracut_config_file): os.remove(self.dracut_config_file) def __del__(self): log.info('Cleaning up %s instance', type(self).__name__) if self.initrd_system == 'dracut': self._delete_dracut_install_config() if self.media_dir: Path.wipe(self.media_dir) if self.pxe_dir: Path.wipe(self.pxe_dir) if self.squashed_contents: Path.wipe(self.squashed_contents)
class LiveImageBuilder(object): """ **Live image builder** :param object xml_state: instance of :class:`XMLState` :param str target_dir: target directory path name :param str root_dir: root directory path name :param dict custom_args: Custom processing arguments """ def __init__(self, xml_state, target_dir, root_dir, custom_args=None): self.media_dir = None self.live_container_dir = None self.arch = platform.machine() if self.arch == 'i686' or self.arch == 'i586': self.arch = 'ix86' self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.live_type = xml_state.build_type.get_flags() self.volume_id = xml_state.build_type.get_volid() or \ Defaults.get_volume_id() self.mbrid = SystemIdentifier() self.mbrid.calculate_id() self.filesystem_custom_parameters = { 'mount_options': xml_state.get_fs_mount_option_list() } self.publisher = xml_state.build_type.get_publisher() or \ Defaults.get_publisher() self.custom_args = custom_args if not self.live_type: self.live_type = Defaults.get_default_live_iso_type() self.boot_image = BootImageDracut(xml_state, target_dir, self.root_dir) self.firmware = FirmWare(xml_state) self.system_setup = SystemSetup(xml_state=xml_state, root_dir=self.root_dir) self.isoname = ''.join([ target_dir, '/', xml_state.xml_data.get_name(), '.' + platform.machine(), '-' + xml_state.get_image_version(), '.iso' ]) self.result = Result(xml_state) self.runtime_config = RuntimeConfig() def create(self): """ Build a bootable hybrid live ISO image Image types which triggers this builder are: * image="iso" :raises KiwiLiveBootImageError: if no kernel or hipervisor is found in boot image tree :return: result :rtype: instance of :class:`Result` """ # media dir to store CD contents self.media_dir = mkdtemp(prefix='live-media.', dir=self.target_dir) # unpack cdroot user files to media dir self.system_setup.import_cdroot_files(self.media_dir) rootsize = SystemSize(self.media_dir) # custom iso metadata log.info('Using following live ISO metadata:') log.info('--> Application id: {0}'.format(self.mbrid.get_id())) log.info('--> Publisher: {0}'.format(Defaults.get_publisher())) log.info('--> Volume id: {0}'.format(self.volume_id)) custom_iso_args = { 'meta_data': { 'publisher': self.publisher, 'preparer': Defaults.get_preparer(), 'volume_id': self.volume_id, 'mbr_id': self.mbrid.get_id(), 'efi_mode': self.firmware.efi_mode() } } # pack system into live boot structure as expected by dracut log.info('Packing system into dracut live ISO type: {0}'.format( self.live_type)) root_filesystem = Defaults.get_default_live_iso_root_filesystem() filesystem_custom_parameters = { 'mount_options': self.xml_state.get_fs_mount_option_list() } filesystem_setup = FileSystemSetup(self.xml_state, self.root_dir) root_image = NamedTemporaryFile() loop_provider = LoopDevice( root_image.name, filesystem_setup.get_size_mbytes(root_filesystem), self.xml_state.build_type.get_target_blocksize()) loop_provider.create() live_filesystem = FileSystem(name=root_filesystem, device_provider=loop_provider, root_dir=self.root_dir + os.sep, custom_args=filesystem_custom_parameters) live_filesystem.create_on_device() log.info('--> Syncing data to {0} root image'.format(root_filesystem)) live_filesystem.sync_data( Defaults.get_exclude_list_for_root_data_sync()) log.info('--> Creating squashfs container for root image') self.live_container_dir = mkdtemp(prefix='live-container.', dir=self.target_dir) Path.create(self.live_container_dir + '/LiveOS') shutil.copy(root_image.name, self.live_container_dir + '/LiveOS/rootfs.img') live_container_image = FileSystem(name='squashfs', device_provider=None, root_dir=self.live_container_dir) container_image = NamedTemporaryFile() live_container_image.create_on_file(container_image.name) Path.create(self.media_dir + '/LiveOS') shutil.copy(container_image.name, self.media_dir + '/LiveOS/squashfs.img') log.info('Setting up live image bootloader configuration') if self.firmware.efi_mode(): # setup bootloader config to boot the ISO via EFI # This also embedds an MBR and the respective BIOS modules # for compat boot. The complete bootloader setup will be # based on grub bootloader_config = BootLoaderConfig( 'grub2', self.xml_state, self.media_dir, { 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) }) bootloader_config.setup_live_boot_images(mbrid=self.mbrid, lookup_path=self.root_dir) else: # setup bootloader config to boot the ISO via isolinux. # This allows for booting on x86 platforms in BIOS mode # only. bootloader_config = BootLoaderConfig('isolinux', self.xml_state, self.media_dir) IsoToolsBase.setup_media_loader_directory( self.boot_image.boot_root_directory, self.media_dir, bootloader_config.get_boot_theme()) bootloader_config.setup_live_image_config(mbrid=self.mbrid) bootloader_config.write() # call custom editbootconfig script if present self.system_setup.call_edit_boot_config_script( filesystem='iso:{0}'.format(self.media_dir), boot_part_id=1, working_directory=self.root_dir) # prepare dracut initrd call self.boot_image.prepare() # create dracut initrd for live image log.info('Creating live ISO boot image') self._create_dracut_live_iso_config() self.boot_image.create_initrd(self.mbrid) # setup kernel file(s) and initrd in ISO boot layout log.info('Setting up kernel file(s) and boot image in ISO boot layout') self._setup_live_iso_kernel_and_initrd() # calculate size and decide if we need UDF if rootsize.accumulate_mbyte_file_sizes() > 4096: log.info('ISO exceeds 4G size, using UDF filesystem') custom_iso_args['meta_data']['udf'] = True # create iso filesystem from media_dir log.info('Creating live ISO image') iso_image = FileSystemIsoFs(device_provider=None, root_dir=self.media_dir, custom_args=custom_iso_args) iso_image.create_on_file(self.isoname) # include metadata for checkmedia tool if self.xml_state.build_type.get_mediacheck() is True: Iso.set_media_tag(self.isoname) self.result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.isoname) self.result.add(key='live_image', filename=self.isoname, use_for_bundle=True, compress=False, shasum=True) self.result.add(key='image_packages', filename=self.system_setup.export_package_list( self.target_dir), use_for_bundle=True, compress=False, shasum=False) self.result.add(key='image_verified', filename=self.system_setup.export_package_verification( self.target_dir), use_for_bundle=True, compress=False, shasum=False) return self.result def _create_dracut_live_iso_config(self): live_config_file = self.root_dir + '/etc/dracut.conf.d/02-livecd.conf' omit_modules = [ 'kiwi-dump', 'kiwi-overlay', 'kiwi-repart', 'kiwi-lib', 'multipath' ] live_config = [ 'add_dracutmodules+=" {0} pollcdrom "'.format( Defaults.get_live_dracut_module_from_flag(self.live_type)), 'omit_dracutmodules+=" {0} "'.format(' '.join(omit_modules)), 'hostonly="no"', 'dracut_rescue_image="no"' ] with open(live_config_file, 'w') as config: for entry in live_config: config.write(entry + os.linesep) def _setup_live_iso_kernel_and_initrd(self): """ Copy kernel and initrd from the root tree into the iso boot structure """ boot_path = ''.join([self.media_dir, '/boot/', self.arch, '/loader']) Path.create(boot_path) # Move kernel files to iso filesystem structure kernel = Kernel(self.boot_image.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(boot_path, '/linux') else: raise KiwiLiveBootImageError( 'No kernel in boot image tree {0} found'.format( self.boot_image.boot_root_directory)) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor(boot_path, '/xen.gz') else: raise KiwiLiveBootImageError( 'No hypervisor in boot image tree {0} found'.format( self.boot_image.boot_root_directory)) # Move initrd to iso filesystem structure if os.path.exists(self.boot_image.initrd_filename): shutil.move(self.boot_image.initrd_filename, boot_path + '/initrd') else: raise KiwiLiveBootImageError( 'No boot image {0} in boot image tree {1} found'.format( self.boot_image.initrd_filename, self.boot_image.boot_root_directory)) def __del__(self): if self.media_dir or self.live_container_dir: log.info('Cleaning up {0} instance'.format(type(self).__name__)) if self.media_dir: Path.wipe(self.media_dir) if self.live_container_dir: Path.wipe(self.live_container_dir)
class TestSystemSetup(object): @patch('platform.machine') def setup(self, mock_machine): mock_machine.return_value = 'x86_64' self.context_manager_mock = mock.Mock() self.file_mock = mock.Mock() self.enter_mock = mock.Mock() self.exit_mock = mock.Mock() self.enter_mock.return_value = self.file_mock setattr(self.context_manager_mock, '__enter__', self.enter_mock) setattr(self.context_manager_mock, '__exit__', self.exit_mock) self.xml_state = mock.MagicMock() self.xml_state.get_package_manager = mock.Mock(return_value='zypper') self.xml_state.build_type.get_filesystem = mock.Mock( return_value='ext3') self.xml_state.xml_data.get_name = mock.Mock(return_value='some-image') self.xml_state.get_image_version = mock.Mock(return_value='1.2.3') self.xml_state.xml_data.description_dir = 'description_dir' self.setup = SystemSetup(self.xml_state, 'root_dir') description = XMLDescription(description='../data/example_config.xml', derived_from='derived/description') self.setup_with_real_xml = SystemSetup(XMLState(description.load()), 'root_dir') command_run = namedtuple('command', ['output', 'error', 'returncode']) self.run_result = command_run(output='password-hash\n', error='stderr', returncode=0) @patch('platform.machine') def test_setup_ix86(self, mock_machine): mock_machine.return_value = 'i686' setup = SystemSetup(mock.MagicMock(), 'root_dir') assert setup.arch == 'ix86' @patch('kiwi.command.Command.run') @patch_open @patch('os.path.exists') @patch('kiwi.system.setup.glob.iglob') def test_import_description(self, mock_iglob, mock_path, mock_open, mock_command): mock_iglob.return_value = ['config-cdroot.tar.xz'] mock_path.return_value = True self.setup_with_real_xml.import_description() mock_iglob.assert_called_once_with('../data/config-cdroot.tar*') assert mock_command.call_args_list == [ call(['mkdir', '-p', 'root_dir/image']), call(['cp', '../data/config.sh', 'root_dir/image/config.sh']), call([ 'cp', '../data/my_edit_boot_script', 'root_dir/image/edit_boot_config.sh' ]), call([ 'cp', '/absolute/path/to/my_edit_boot_install', 'root_dir/image/edit_boot_install.sh' ]), call(['cp', '../data/images.sh', 'root_dir/image/images.sh']), call([ 'cp', Defaults.project_file('config/functions.sh'), 'root_dir/.kconfig' ]), call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']), call(['cp', '../data/bootstrap.tgz', 'root_dir/image/']), call(['cp', 'config-cdroot.tar.xz', 'root_dir/image/']) ] @patch('kiwi.command.Command.run') @patch_open @patch('os.path.exists') def test_import_description_archive_from_derived(self, mock_path, mock_open, mock_command): path_return_values = [True, False, True, True, True, True, True] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect self.setup_with_real_xml.import_description() assert mock_command.call_args_list == [ call(['mkdir', '-p', 'root_dir/image']), call(['cp', '../data/config.sh', 'root_dir/image/config.sh']), call([ 'cp', '../data/my_edit_boot_script', 'root_dir/image/edit_boot_config.sh' ]), call([ 'cp', '/absolute/path/to/my_edit_boot_install', 'root_dir/image/edit_boot_install.sh' ]), call(['cp', '../data/images.sh', 'root_dir/image/images.sh']), call([ 'cp', Defaults.project_file('config/functions.sh'), 'root_dir/.kconfig' ]), call(['cp', '/absolute/path/to/image.tgz', 'root_dir/image/']), call( ['cp', 'derived/description/bootstrap.tgz', 'root_dir/image/']) ] @patch('kiwi.command.Command.run') @patch_open @patch('os.path.exists') @raises(KiwiImportDescriptionError) def test_import_description_configured_editboot_scripts_not_found( self, mock_path, mock_open, mock_command): path_return_values = [False, True, True] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect self.setup_with_real_xml.import_description() @patch('kiwi.command.Command.run') @patch_open @patch('os.path.exists') @raises(KiwiImportDescriptionError) def test_import_description_configured_archives_not_found( self, mock_path, mock_open, mock_command): path_return_values = [False, False, True, True, True, True] def side_effect(arg): return path_return_values.pop() mock_path.side_effect = side_effect self.setup_with_real_xml.import_description() @patch('kiwi.command.Command.run') def test_cleanup(self, mock_command): self.setup.cleanup() mock_command.assert_called_once_with( ['rm', '-r', '-f', '/.kconfig', '/image']) @patch_open def test_import_shell_environment(self, mock_open): mock_profile = mock.MagicMock() mock_profile.create = mock.Mock(return_value=['a']) mock_open.return_value = self.context_manager_mock self.setup.import_shell_environment(mock_profile) mock_profile.create.assert_called_once_with() mock_open.assert_called_once_with('root_dir/.profile', 'w') self.file_mock.write.assert_called_once_with('a\n') @patch('kiwi.system.setup.ArchiveTar') @patch('kiwi.system.setup.glob.iglob') def test_import_cdroot_files(self, mock_iglob, mock_ArchiveTar): archive = mock.Mock() mock_ArchiveTar.return_value = archive mock_iglob.return_value = ['config-cdroot.tar.xz'] self.setup.import_cdroot_files('target_dir') mock_iglob.assert_called_once_with( 'description_dir/config-cdroot.tar*') mock_ArchiveTar.assert_called_once_with('config-cdroot.tar.xz') archive.extract.assert_called_once_with('target_dir') @patch('kiwi.command.Command.run') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_import_overlay_files_copy_links(self, mock_os_path, mock_DataSync, mock_command): data = mock.Mock() mock_DataSync.return_value = data mock_os_path.return_value = True self.setup.import_overlay_files(follow_links=True, preserve_owner_group=True) mock_DataSync.assert_called_once_with('description_dir/root/', 'root_dir') data.sync_data.assert_called_once_with(options=[ '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system', '--copy-links', '-o', '-g' ]) @patch('kiwi.command.Command.run') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_import_overlay_files_links(self, mock_os_path, mock_DataSync, mock_command): data = mock.Mock() mock_DataSync.return_value = data mock_os_path.return_value = True self.setup.import_overlay_files(follow_links=False, preserve_owner_group=True) mock_DataSync.assert_called_once_with('description_dir/root/', 'root_dir') data.sync_data.assert_called_once_with(options=[ '-r', '-p', '-t', '-D', '-H', '-X', '-A', '--one-file-system', '--links', '-o', '-g' ]) @patch('kiwi.system.setup.ArchiveTar') @patch('os.path.exists') def test_import_overlay_files_from_archive(self, mock_os_path, mock_archive): archive = mock.Mock() mock_archive.return_value = archive exists_results = [True, False] def side_effect(arg): return exists_results.pop() mock_os_path.side_effect = side_effect self.setup.import_overlay_files() mock_archive.assert_called_once_with('description_dir/root.tar.gz') archive.extract.assert_called_once_with('root_dir') @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_keyboard_map(self, mock_path, mock_run, mock_shell): mock_path.return_value = True self.setup.preferences['keytable'] = 'keytable' self.setup.setup_keyboard_map() mock_shell.assert_called_once_with( 'baseUpdateSysConfig', ['root_dir/etc/sysconfig/keyboard', 'KEYTABLE', '"keytable"']) @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_keyboard_map_with_systemd(self, mock_path, mock_run, mock_shell, mock_caps): mock_caps.return_value = True mock_path.return_value = True self.setup.preferences['keytable'] = 'keytable' self.setup.setup_keyboard_map() mock_run.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/vconsole.conf']), call([ 'chroot', 'root_dir', 'systemd-firstboot', '--keymap=keytable' ]) ]) @patch('kiwi.logger.log.warning') @patch('os.path.exists') def test_setup_keyboard_skipped(self, mock_exists, mock_log_warn): mock_exists.return_value = False self.setup.preferences['keytable'] = 'keytable' self.setup.setup_keyboard_map() assert mock_log_warn.called @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_locale(self, mock_path, mock_run, mock_shell, mock_caps): mock_caps.return_valure = True mock_path.return_value = True self.setup.preferences['locale'] = 'locale1,locale2' self.setup.setup_locale() mock_run.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/locale.conf']), call([ 'chroot', 'root_dir', 'systemd-firstboot', '--locale=locale1.UTF-8' ]) ]) mock_shell.assert_called_once_with( 'baseUpdateSysConfig', ['root_dir/etc/sysconfig/language', 'RC_LANG', 'locale1.UTF-8']) @patch('kiwi.system.setup.Shell.run_common_function') @patch('kiwi.system.setup.Command.run') @patch('os.path.exists') def test_setup_locale_POSIX(self, mock_path, mock_run, mock_shell): mock_path.return_value = True self.setup.preferences['locale'] = 'POSIX,locale2' self.setup.setup_locale() mock_shell.assert_called_once_with( 'baseUpdateSysConfig', ['root_dir/etc/sysconfig/language', 'RC_LANG', 'POSIX']) @patch('kiwi.system.setup.Command.run') def test_setup_timezone(self, mock_command): self.setup.preferences['timezone'] = 'timezone' self.setup.setup_timezone() mock_command.assert_has_calls([ call([ 'chroot', 'root_dir', 'ln', '-s', '-f', '/usr/share/zoneinfo/timezone', '/etc/localtime' ]) ]) @patch('kiwi.system.setup.CommandCapabilities.has_option_in_help') @patch('kiwi.system.setup.Command.run') def test_setup_timezone_with_systemd(self, mock_command, mock_caps): mock_caps.return_value = True self.setup.preferences['timezone'] = 'timezone' self.setup.setup_timezone() mock_command.assert_has_calls([ call(['rm', '-r', '-f', 'root_dir/etc/localtime']), call([ 'chroot', 'root_dir', 'systemd-firstboot', '--timezone=timezone' ]) ]) @patch('kiwi.system.setup.Users') def test_setup_groups(self, mock_users): users = mock.Mock() users.group_exists = mock.Mock(return_value=False) mock_users.return_value = users self.setup_with_real_xml.setup_groups() calls = [call('users'), call('kiwi'), call('admin')] users.group_exists.assert_has_calls(calls) calls = [call('users', []), call('kiwi', []), call('admin', [])] users.group_add.assert_has_calls(calls) @patch('kiwi.system.setup.Users') @patch('kiwi.system.setup.Command.run') def test_setup_users_add(self, mock_command, mock_users): users = mock.Mock() users.user_exists = mock.Mock(return_value=False) mock_users.return_value = users mock_command.return_value = self.run_result self.setup_with_real_xml.setup_users() calls = [call('root'), call('tux'), call('kiwi')] users.user_exists.assert_has_calls(calls) calls = [ call('root', [ '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c', 'Bob', '-m', '-d', '/root' ]), call('tux', [ '-p', 'password-hash', '-g', 'users', '-m', '-d', '/home/tux' ]), call('kiwi', [ '-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users', '-m', '-d', '/home/kiwi' ]) ] users.user_add.assert_has_calls(calls) mock_command.assert_called_with( ['openssl', 'passwd', '-1', '-salt', 'xyz', 'mypwd']) @patch('kiwi.system.setup.Users') @patch('kiwi.system.setup.Command.run') def test_setup_users_modify(self, mock_command, mock_users): users = mock.Mock() users.user_exists = mock.Mock(return_value=True) mock_users.return_value = users mock_command.return_value = self.run_result self.setup_with_real_xml.setup_users() calls = [call('root'), call('tux'), call('kiwi')] users.user_exists.assert_has_calls(calls) calls = [ call('root', [ '-p', 'password-hash', '-s', '/bin/bash', '-u', '815', '-c', 'Bob' ]), call('tux', ['-p', 'password-hash', '-g', 'users']), call('kiwi', ['-p', 'password-hash', '-g', 'kiwi', '-G', 'admin,users']) ] users.user_modify.assert_has_calls(calls) @patch('kiwi.system.setup.Path.which') @patch('kiwi.system.setup.Command.run') def test_setup_plymouth_splash(self, mock_command, mock_which): mock_which.return_value = 'plymouth-set-default-theme' preferences = mock.Mock() preferences.get_bootsplash_theme = mock.Mock( return_value=['some-theme']) self.xml_state.get_preferences_sections = mock.Mock( return_value=[preferences]) self.setup.setup_plymouth_splash() mock_which.assert_called_once_with( custom_env={'PATH': 'root_dir/usr/sbin'}, filename='plymouth-set-default-theme') mock_command.assert_called_once_with( ['chroot', 'root_dir', 'plymouth-set-default-theme', 'some-theme']) @patch_open @patch('os.path.exists') def test_import_image_identifier(self, mock_os_path, mock_open): self.xml_state.xml_data.get_id = mock.Mock(return_value='42') mock_os_path.return_value = True mock_open.return_value = self.context_manager_mock self.setup.import_image_identifier() mock_open.assert_called_once_with('root_dir/etc/ImageID', 'w') self.file_mock.write.assert_called_once_with('42\n') @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') def test_call_non_excutable_config_script(self, mock_access, mock_stat, mock_os_path, mock_watch, mock_command): result_type = namedtuple('result', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result mock_access.return_value = False self.setup.call_config_script() mock_command.assert_called_once_with( ['chroot', 'root_dir', 'bash', '/image/config.sh']) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') def test_call_excutable_config_script(self, mock_access, mock_stat, mock_os_path, mock_watch, mock_command): result_type = namedtuple('result', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result # pretend that the script is executable mock_access.return_value = True self.setup.call_config_script() mock_command.assert_called_once_with( ['chroot', 'root_dir', '/image/config.sh']) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') def test_call_image_script(self, mock_access, mock_stat, mock_os_path, mock_watch, mock_command): result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_os_path.return_value = True mock_watch.return_value = mock_result mock_access.return_value = False self.setup.call_image_script() mock_command.assert_called_once_with( ['chroot', 'root_dir', 'bash', '/image/images.sh']) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.path.abspath') def test_call_edit_boot_config_script(self, mock_abspath, mock_exists, mock_watch, mock_command): result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_exists.return_value = True mock_abspath.return_value = '/root_dir/image/edit_boot_config.sh' mock_watch.return_value = mock_result self.setup.call_edit_boot_config_script('ext4', 1) mock_abspath.assert_called_once_with( 'root_dir/image/edit_boot_config.sh') mock_command.assert_called_once_with([ 'bash', '-c', 'cd root_dir && bash --norc /root_dir/image/edit_boot_config.sh ' 'ext4 1' ]) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.path.abspath') def test_call_edit_boot_install_script(self, mock_abspath, mock_exists, mock_watch, mock_command): result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=0) mock_exists.return_value = True mock_abspath.return_value = '/root_dir/image/edit_boot_install.sh' mock_watch.return_value = mock_result self.setup.call_edit_boot_install_script('my_image.raw', '/dev/mapper/loop0p1') mock_abspath.assert_called_once_with( 'root_dir/image/edit_boot_install.sh') mock_command.assert_called_once_with([ 'bash', '-c', 'cd root_dir && bash --norc /root_dir/image/edit_boot_install.sh ' 'my_image.raw /dev/mapper/loop0p1' ]) @raises(KiwiScriptFailed) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') @patch('os.stat') @patch('os.access') def test_call_image_script_raises(self, mock_access, mock_stat, mock_os_path, mock_watch, mock_command): result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=1) mock_os_path.return_value = True mock_watch.return_value = mock_result mock_access.return_value = False self.setup.call_image_script() @raises(KiwiScriptFailed) @patch('kiwi.command.Command.call') @patch('kiwi.command_process.CommandProcess.poll_and_watch') @patch('os.path.exists') def test_call_edit_boot_install_script_raises(self, mock_os_path, mock_watch, mock_command): result_type = namedtuple('result_type', ['stderr', 'returncode']) mock_result = result_type(stderr='stderr', returncode=1) mock_os_path.return_value = True mock_watch.return_value = mock_result self.setup.call_edit_boot_install_script('my_image.raw', '/dev/mapper/loop0p1') @patch('kiwi.command.Command.run') def test_create_init_link_from_linuxrc(self, mock_command): self.setup.create_init_link_from_linuxrc() mock_command.assert_called_once_with( ['ln', 'root_dir/linuxrc', 'root_dir/init']) @patch('kiwi.command.Command.run') def test_create_recovery_archive_cleanup_only(self, mock_command): self.setup.oemconfig['recovery'] = False self.setup.create_recovery_archive() assert mock_command.call_args_list[0] == call( ['bash', '-c', 'rm -f root_dir/recovery.*']) @patch_open @patch('os.path.exists') @patch('kiwi.system.setup.Path.wipe') @patch('kiwi.command.Command.run') def test_create_fstab(self, mock_command, mock_wipe, mock_exists, mock_open): mock_exists.return_value = True mock_open.return_value = self.context_manager_mock self.file_mock.read.return_value = 'append_entry' self.setup.create_fstab(['fstab_entry']) assert mock_open.call_args_list == [ call('root_dir/etc/fstab', 'w'), call('root_dir/etc/fstab.append', 'r') ] assert self.file_mock.write.call_args_list == [ call('fstab_entry\n'), call('append_entry') ] mock_command.assert_called_once_with( ['patch', 'root_dir/etc/fstab', 'root_dir/etc/fstab.patch']) assert mock_wipe.call_args_list == [ call('root_dir/etc/fstab.append'), call('root_dir/etc/fstab.patch') ] @patch('kiwi.command.Command.run') @patch('kiwi.system.setup.NamedTemporaryFile') @patch('kiwi.system.setup.ArchiveTar') @patch_open @patch('kiwi.system.setup.Compress') @patch('os.path.getsize') @patch('kiwi.system.setup.Path.wipe') def test_create_recovery_archive(self, mock_wipe, mock_getsize, mock_compress, mock_open, mock_archive, mock_temp, mock_command): mock_open.return_value = self.context_manager_mock mock_getsize.return_value = 42 compress = mock.Mock() mock_compress.return_value = compress archive = mock.Mock() mock_archive.return_value = archive tmpdir = mock.Mock() tmpdir.name = 'tmpdir' mock_temp.return_value = tmpdir self.setup.oemconfig['recovery'] = True self.setup.oemconfig['recovery_inplace'] = True self.setup.create_recovery_archive() assert mock_command.call_args_list[0] == call( ['bash', '-c', 'rm -f root_dir/recovery.*']) mock_archive.assert_called_once_with(create_from_file_list=False, filename='tmpdir') archive.create.assert_called_once_with(exclude=['dev', 'proc', 'sys'], options=[ '--numeric-owner', '--hard-dereference', '--preserve-permissions' ], source_dir='root_dir') assert mock_command.call_args_list[1] == call( ['mv', 'tmpdir', 'root_dir/recovery.tar']) assert mock_open.call_args_list[0] == call( 'root_dir/recovery.tar.filesystem', 'w') assert self.file_mock.write.call_args_list[0] == call('ext3') assert mock_command.call_args_list[2] == call( ['bash', '-c', 'tar -tf root_dir/recovery.tar | wc -l']) assert mock_open.call_args_list[1] == call( 'root_dir/recovery.tar.files', 'w') assert mock_getsize.call_args_list[0] == call('root_dir/recovery.tar') assert self.file_mock.write.call_args_list[1] == call('1\n') assert mock_open.call_args_list[2] == call( 'root_dir/recovery.tar.size', 'w') assert self.file_mock.write.call_args_list[2] == call('42') mock_compress.assert_called_once_with('root_dir/recovery.tar') compress.gzip.assert_called_once_with() assert mock_getsize.call_args_list[1] == call( 'root_dir/recovery.tar.gz') assert mock_open.call_args_list[3] == call( 'root_dir/recovery.partition.size', 'w') assert self.file_mock.write.call_args_list[3] == call('300') mock_wipe.assert_called_once_with('root_dir/recovery.tar.gz') @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.Path.create') @patch('kiwi.system.setup.DataSync') @patch('os.path.exists') def test_export_modprobe_setup(self, mock_exists, mock_DataSync, mock_path, mock_command): data = mock.Mock() mock_DataSync.return_value = data mock_exists.return_value = True self.setup.export_modprobe_setup('target_root_dir') mock_path.assert_called_once_with('target_root_dir/etc') mock_DataSync.assert_called_once_with('root_dir/etc/modprobe.d', 'target_root_dir/etc/') data.sync_data.assert_called_once_with(options=['-a']) @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.RpmDataBase') @patch('kiwi.system.setup.MountManager') @patch_open def test_export_package_list_rpm(self, mock_open, mock_MountManager, mock_RpmDataBase, mock_command): rpmdb = mock.Mock() rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath' rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath' rpmdb.has_rpm.return_value = True mock_RpmDataBase.return_value = rpmdb command = mock.Mock() command.output = 'packages_data' mock_command.return_value = command result = self.setup.export_package_list('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_called_once_with([ 'rpm', '--root', 'root_dir', '-qa', '--qf', '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|' '%{DISTURL}|%{LICENSE}\\n', '--dbpath', 'image_dbpath' ]) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.packages', 'w') rpmdb.has_rpm.return_value = False mock_command.reset_mock() result = self.setup.export_package_list('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_called_once_with([ 'rpm', '--root', 'root_dir', '-qa', '--qf', '%{NAME}|%{EPOCH}|%{VERSION}|%{RELEASE}|%{ARCH}|' '%{DISTURL}|%{LICENSE}\\n', '--dbpath', 'host_dbpath' ]) @patch('kiwi.system.setup.os.path.exists') @patch_open def test_setup_machine_id(self, mock_open, mock_path_exists): mock_path_exists.return_value = True self.setup.setup_machine_id() mock_path_exists.return_value = False self.setup.setup_machine_id() mock_open.assert_called_once_with('root_dir/etc/machine-id', 'w') @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.Path.which') @patch('kiwi.logger.log.warning') def test_setup_permissions(self, mock_log_warn, mock_path_which, mock_command): mock_path_which.return_value = 'chkstat' self.setup.setup_permissions() mock_command.assert_called_once_with( ['chroot', 'root_dir', 'chkstat', '--system', '--set']) mock_path_which.return_value = None self.setup.setup_permissions() mock_log_warn.assert_called_once() @patch('kiwi.system.setup.Command.run') @patch_open def test_export_package_list_dpkg(self, mock_open, mock_command): command = mock.Mock() command.output = 'packages_data' mock_command.return_value = command self.xml_state.get_package_manager = mock.Mock(return_value='apt-get') result = self.setup.export_package_list('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.packages' mock_command.assert_called_once_with([ 'dpkg-query', '--admindir', 'root_dir/var/lib/dpkg', '-W', '-f', '${Package}|None|${Version}|None|${Architecture}|None|None\\n' ]) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.packages', 'w') @patch('kiwi.system.setup.Command.run') @patch('kiwi.system.setup.RpmDataBase') @patch('kiwi.system.setup.MountManager') @patch_open def test_export_package_verification(self, mock_open, mock_MountManager, mock_RpmDataBase, mock_command): is_mounted_return = [True, False] def is_mounted(): return is_mounted_return.pop() shared_mount = mock.Mock() shared_mount.is_mounted.side_effect = is_mounted mock_MountManager.return_value = shared_mount rpmdb = mock.Mock() rpmdb.rpmdb_image.expand_query.return_value = 'image_dbpath' rpmdb.rpmdb_host.expand_query.return_value = 'host_dbpath' rpmdb.has_rpm.return_value = True mock_RpmDataBase.return_value = rpmdb command = mock.Mock() command.output = 'verification_data' mock_command.return_value = command result = self.setup.export_package_verification('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_called_once_with(command=[ 'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'image_dbpath' ], raise_on_error=False) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.verified', 'w') mock_MountManager.assert_called_once_with(device='/dev', mountpoint='root_dir/dev') shared_mount.bind_mount.assert_called_once_with() shared_mount.umount_lazy.assert_called_once_with() rpmdb.has_rpm.return_value = False is_mounted_return = [True, False] mock_command.reset_mock() shared_mount.reset_mock() result = self.setup.export_package_verification('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_called_once_with(command=[ 'rpm', '--root', 'root_dir', '-Va', '--dbpath', 'host_dbpath' ], raise_on_error=False) shared_mount.bind_mount.assert_called_once_with() shared_mount.umount_lazy.assert_called_once_with() @patch('kiwi.system.setup.Command.run') @patch_open def test_export_package_verification_dpkg(self, mock_open, mock_command): command = mock.Mock() command.output = 'verification_data' mock_command.return_value = command self.xml_state.get_package_manager = mock.Mock(return_value='apt-get') result = self.setup.export_package_verification('target_dir') assert result == 'target_dir/some-image.x86_64-1.2.3.verified' mock_command.assert_called_once_with(command=[ 'dpkg', '--root', 'root_dir', '-V', '--verify-format', 'rpm' ], raise_on_error=False) mock_open.assert_called_once_with( 'target_dir/some-image.x86_64-1.2.3.verified', 'w') @patch('kiwi.system.setup.Command.run') def test_set_selinux_file_contexts(self, mock_command): self.setup.set_selinux_file_contexts('security_context_file') mock_command.assert_called_once_with([ 'chroot', 'root_dir', 'setfiles', 'security_context_file', '/', '-v' ]) @patch('kiwi.system.setup.Repository') @patch('kiwi.system.setup.Uri') def test_import_repositories_marked_as_imageinclude( self, mock_uri, mock_repo): uri = mock.Mock() mock_uri.return_value = uri uri.translate = mock.Mock(return_value="uri") uri.alias = mock.Mock(return_value="uri-alias") uri.credentials_file_name = mock.Mock( return_value='kiwiRepoCredentials') mock_uri.return_value = uri repo = mock.Mock() mock_repo.return_value = repo self.setup_with_real_xml.import_repositories_marked_as_imageinclude() assert repo.add_repo.call_args_list[0] == call('uri-alias', 'uri', 'rpm-md', None, None, None, None, None, 'kiwiRepoCredentials', None, None)
class LiveImageBuilder(object): """ **Live image builder** :param object xml_state: instance of :class:`XMLState` :param str target_dir: target directory path name :param str root_dir: root directory path name :param dict custom_args: Custom processing arguments defined as hash keys: * xz_options: string of XZ compression parameters """ def __init__(self, xml_state, target_dir, root_dir, custom_args=None): self.media_dir = None self.live_container_dir = None self.arch = platform.machine() if self.arch == 'i686' or self.arch == 'i586': self.arch = 'ix86' self.root_dir = root_dir self.target_dir = target_dir self.xml_state = xml_state self.live_type = xml_state.build_type.get_flags() self.volume_id = xml_state.build_type.get_volid() or \ Defaults.get_volume_id() self.mbrid = SystemIdentifier() self.mbrid.calculate_id() self.filesystem_custom_parameters = { 'mount_options': xml_state.get_fs_mount_option_list() } self.publisher = xml_state.build_type.get_publisher() or \ Defaults.get_publisher() if not self.live_type: self.live_type = Defaults.get_default_live_iso_type() self.boot_image = BootImageDracut( xml_state, target_dir, self.root_dir ) self.firmware = FirmWare( xml_state ) self.system_setup = SystemSetup( xml_state=xml_state, root_dir=self.root_dir ) self.isoname = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '.' + platform.machine(), '-' + xml_state.get_image_version(), '.iso' ] ) self.result = Result(xml_state) self.runtime_config = RuntimeConfig() def create(self): """ Build a bootable hybrid live ISO image Image types which triggers this builder are: * image="iso" :raises KiwiLiveBootImageError: if no kernel or hipervisor is found in boot image tree :return: result :rtype: instance of :class:`Result` """ # media dir to store CD contents self.media_dir = mkdtemp( prefix='live-media.', dir=self.target_dir ) # unpack cdroot user files to media dir self.system_setup.import_cdroot_files(self.media_dir) rootsize = SystemSize(self.media_dir) # custom iso metadata log.info('Using following live ISO metadata:') log.info('--> Application id: {0}'.format(self.mbrid.get_id())) log.info('--> Publisher: {0}'.format(Defaults.get_publisher())) log.info('--> Volume id: {0}'.format(self.volume_id)) custom_iso_args = { 'meta_data': { 'publisher': self.publisher, 'preparer': Defaults.get_preparer(), 'volume_id': self.volume_id, 'mbr_id': self.mbrid.get_id(), 'efi_mode': self.firmware.efi_mode() } } # pack system into live boot structure as expected by dracut log.info( 'Packing system into dracut live ISO type: {0}'.format( self.live_type ) ) root_filesystem = Defaults.get_default_live_iso_root_filesystem() filesystem_custom_parameters = { 'mount_options': self.xml_state.get_fs_mount_option_list() } filesystem_setup = FileSystemSetup( self.xml_state, self.root_dir ) root_image = NamedTemporaryFile() loop_provider = LoopDevice( root_image.name, filesystem_setup.get_size_mbytes(root_filesystem), self.xml_state.build_type.get_target_blocksize() ) loop_provider.create() live_filesystem = FileSystem( name=root_filesystem, device_provider=loop_provider, root_dir=self.root_dir + os.sep, custom_args=filesystem_custom_parameters ) live_filesystem.create_on_device() log.info( '--> Syncing data to {0} root image'.format(root_filesystem) ) live_filesystem.sync_data( Defaults.get_exclude_list_for_root_data_sync() ) log.info('--> Creating squashfs container for root image') self.live_container_dir = mkdtemp( prefix='live-container.', dir=self.target_dir ) Path.create(self.live_container_dir + '/LiveOS') shutil.copy( root_image.name, self.live_container_dir + '/LiveOS/rootfs.img' ) live_container_image = FileSystem( name='squashfs', device_provider=None, root_dir=self.live_container_dir ) container_image = NamedTemporaryFile() live_container_image.create_on_file( container_image.name ) Path.create(self.media_dir + '/LiveOS') shutil.copy( container_image.name, self.media_dir + '/LiveOS/squashfs.img' ) # setup bootloader config to boot the ISO via isolinux log.info('Setting up isolinux bootloader configuration') bootloader_config_isolinux = BootLoaderConfig( 'isolinux', self.xml_state, self.media_dir ) bootloader_config_isolinux.setup_live_boot_images( mbrid=None, lookup_path=self.boot_image.boot_root_directory ) bootloader_config_isolinux.setup_live_image_config( mbrid=None ) bootloader_config_isolinux.write() # setup bootloader config to boot the ISO via EFI if self.firmware.efi_mode(): log.info('Setting up EFI grub bootloader configuration') bootloader_config_grub = BootLoaderConfig( 'grub2', self.xml_state, self.media_dir, { 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) } ) bootloader_config_grub.setup_live_boot_images( mbrid=self.mbrid, lookup_path=self.root_dir ) bootloader_config_grub.setup_live_image_config( mbrid=self.mbrid ) bootloader_config_grub.write() # call custom editbootconfig script if present self.system_setup.call_edit_boot_config_script( filesystem='iso:{0}'.format(self.media_dir), boot_part_id=1, working_directory=self.root_dir ) # prepare dracut initrd call self.boot_image.prepare() # create dracut initrd for live image log.info('Creating live ISO boot image') self._create_dracut_live_iso_config() self.boot_image.create_initrd(self.mbrid) # setup kernel file(s) and initrd in ISO boot layout log.info('Setting up kernel file(s) and boot image in ISO boot layout') self._setup_live_iso_kernel_and_initrd() # calculate size and decide if we need UDF if rootsize.accumulate_mbyte_file_sizes() > 4096: log.info('ISO exceeds 4G size, using UDF filesystem') custom_iso_args['meta_data']['udf'] = True # create iso filesystem from media_dir log.info('Creating live ISO image') iso_image = FileSystemIsoFs( device_provider=None, root_dir=self.media_dir, custom_args=custom_iso_args ) iso_image.create_on_file(self.isoname) # include metadata for checkmedia tool if self.xml_state.build_type.get_mediacheck() is True: Iso.set_media_tag(self.isoname) self.result.verify_image_size( self.runtime_config.get_max_size_constraint(), self.isoname ) self.result.add( key='live_image', filename=self.isoname, use_for_bundle=True, compress=False, shasum=True ) self.result.add( key='image_packages', filename=self.system_setup.export_package_list( self.target_dir ), use_for_bundle=True, compress=False, shasum=False ) self.result.add( key='image_verified', filename=self.system_setup.export_package_verification( self.target_dir ), use_for_bundle=True, compress=False, shasum=False ) return self.result def _create_dracut_live_iso_config(self): live_config_file = self.root_dir + '/etc/dracut.conf.d/02-livecd.conf' omit_modules = [ 'kiwi-dump', 'kiwi-overlay', 'kiwi-repart', 'kiwi-lib', 'multipath' ] live_config = [ 'add_dracutmodules+=" {0} pollcdrom "'.format( Defaults.get_live_dracut_module_from_flag(self.live_type) ), 'omit_dracutmodules+=" {0} "'.format(' '.join(omit_modules)), 'hostonly="no"', 'dracut_rescue_image="no"' ] with open(live_config_file, 'w') as config: for entry in live_config: config.write(entry + os.linesep) def _setup_live_iso_kernel_and_initrd(self): """ Copy kernel and initrd from the root tree into the iso boot structure """ boot_path = ''.join( [self.media_dir, '/boot/', self.arch, '/loader'] ) Path.create(boot_path) # Move kernel files to iso filesystem structure kernel = Kernel(self.boot_image.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(boot_path, '/linux') else: raise KiwiLiveBootImageError( 'No kernel in boot image tree {0} found'.format( self.boot_image.boot_root_directory ) ) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor(boot_path, '/xen.gz') else: raise KiwiLiveBootImageError( 'No hypervisor in boot image tree {0} found'.format( self.boot_image.boot_root_directory ) ) # Move initrd to iso filesystem structure if os.path.exists(self.boot_image.initrd_filename): shutil.move( self.boot_image.initrd_filename, boot_path + '/initrd' ) else: raise KiwiLiveBootImageError( 'No boot image {0} in boot image tree {1} found'.format( self.boot_image.initrd_filename, self.boot_image.boot_root_directory ) ) def __del__(self): if self.media_dir or self.live_container_dir: log.info( 'Cleaning up {0} instance'.format(type(self).__name__) ) if self.media_dir: Path.wipe(self.media_dir) if self.live_container_dir: Path.wipe(self.live_container_dir)
class InstallImageBuilder(object): """ **Installation image builder** :param object xml_state: instance of :class:`XMLState` :param str root_dir: system image root directory :param str target_dir: target directory path name :param object boot_image_task: instance of :class:`BootImage` :param dict custom_args: Custom processing arguments defined as hash keys: * xz_options: string of XZ compression parameters """ def __init__( self, xml_state, root_dir, target_dir, boot_image_task, custom_args=None ): self.arch = platform.machine() if self.arch == 'i686' or self.arch == 'i586': self.arch = 'ix86' self.root_dir = root_dir self.target_dir = target_dir self.boot_image_task = boot_image_task self.xml_state = xml_state self.root_filesystem_is_multipath = \ xml_state.get_oemconfig_oem_multipath_scan() self.initrd_system = xml_state.get_initrd_system() self.firmware = FirmWare(xml_state) self.setup = SystemSetup( self.xml_state, self.root_dir ) self.diskname = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version(), '.raw' ] ) self.isoname = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version(), '.install.iso' ] ) self.pxename = ''.join( [ target_dir, '/', xml_state.xml_data.get_name(), '.' + self.arch, '-' + xml_state.get_image_version(), '.install.tar.xz' ] ) self.dracut_config_file = ''.join( [self.root_dir, Defaults.get_dracut_conf_name()] ) self.squashed_diskname = ''.join( [xml_state.xml_data.get_name(), '.raw'] ) self.md5name = ''.join( [xml_state.xml_data.get_name(), '.md5'] ) self.xz_options = custom_args['xz_options'] if custom_args \ and 'xz_options' in custom_args else None self.mbrid = SystemIdentifier() self.mbrid.calculate_id() self.media_dir = None self.pxe_dir = None self.squashed_contents = None self.custom_iso_args = None def create_install_iso(self): """ Create an install ISO from the disk_image as hybrid ISO bootable via legacy BIOS, EFI and as disk from Stick Image types which triggers this builder are: * installiso="true|false" * installstick="true|false" """ self.media_dir = mkdtemp( prefix='kiwi_install_media.', dir=self.target_dir ) # unpack cdroot user files to media dir self.setup.import_cdroot_files(self.media_dir) # custom iso metadata self.custom_iso_args = { 'meta_data': { 'volume_id': Defaults.get_install_volume_id(), 'mbr_id': self.mbrid.get_id(), 'efi_mode': self.firmware.efi_mode() } } # the system image transfer is checked against a checksum log.info('Creating disk image checksum') self.squashed_contents = mkdtemp( prefix='kiwi_install_squashfs.', dir=self.target_dir ) checksum = Checksum(self.diskname) checksum.md5(self.squashed_contents + '/' + self.md5name) # the system image name is stored in a config file self._write_install_image_info_to_iso_image() if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the system image is stored as squashfs embedded file log.info('Creating squashfs embedded disk image') Command.run( [ 'cp', '-l', self.diskname, self.squashed_contents + '/' + self.squashed_diskname ] ) squashed_image_file = ''.join( [ self.target_dir, '/', self.squashed_diskname, '.squashfs' ] ) squashed_image = FileSystemSquashFs( device_provider=None, root_dir=self.squashed_contents ) squashed_image.create_on_file(squashed_image_file) Command.run( ['mv', squashed_image_file, self.media_dir] ) # setup bootloader config to boot the ISO via isolinux log.info('Setting up install image bootloader configuration') bootloader_config_isolinux = BootLoaderConfig( 'isolinux', self.xml_state, self.media_dir ) bootloader_config_isolinux.setup_install_boot_images( mbrid=None, lookup_path=self.boot_image_task.boot_root_directory ) bootloader_config_isolinux.setup_install_image_config( mbrid=None ) bootloader_config_isolinux.write() # setup bootloader config to boot the ISO via EFI bootloader_config_grub = BootLoaderConfig( 'grub2', self.xml_state, self.media_dir, { 'grub_directory_name': Defaults.get_grub_boot_directory_name(self.root_dir) } ) bootloader_config_grub.setup_install_boot_images( mbrid=self.mbrid, lookup_path=self.root_dir ) bootloader_config_grub.setup_install_image_config( mbrid=self.mbrid ) bootloader_config_grub.write() # create initrd for install image log.info('Creating install image boot image') self._create_iso_install_kernel_and_initrd() # the system image initrd is stored to allow kexec self._copy_system_image_initrd_to_iso_image() # create iso filesystem from media_dir log.info('Creating ISO filesystem') iso_image = FileSystemIsoFs( device_provider=None, root_dir=self.media_dir, custom_args=self.custom_iso_args ) iso_image.create_on_file(self.isoname) def create_install_pxe_archive(self): """ Create an oem install tar archive suitable for installing a disk image via the network using the PXE boot protocol. The archive contains: * The raw system image xz compressed * The raw system image checksum metadata file * The append file template for the boot server * The system image initrd for kexec * The install initrd * The kernel Image types which triggers this builder are: * installpxe="true|false" """ self.pxe_dir = mkdtemp( prefix='kiwi_pxe_install_media.', dir=self.target_dir ) # the system image is transfered as xz compressed variant log.info('xz compressing disk image') pxe_image_filename = ''.join( [ self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.xz' ] ) compress = Compress( source_filename=self.diskname, keep_source_on_compress=True ) compress.xz(self.xz_options) Command.run( ['mv', compress.compressed_filename, pxe_image_filename] ) # the system image transfer is checked against a checksum log.info('Creating disk image checksum') pxe_md5_filename = ''.join( [ self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.md5' ] ) checksum = Checksum(self.diskname) checksum.md5(pxe_md5_filename) # the install image name is stored in a config file if self.initrd_system == 'kiwi': self._write_install_image_info_to_boot_image() # the kexec required system image initrd is stored for dracut kiwi-dump if self.initrd_system == 'dracut': boot_names = self.boot_image_task.get_boot_names() system_image_initrd = os.sep.join( [self.root_dir, 'boot', boot_names.initrd_name] ) target_initrd_name = '{0}/{1}.initrd'.format( self.pxe_dir, self.xml_state.xml_data.get_name() ) shutil.copy( system_image_initrd, target_initrd_name ) os.chmod(target_initrd_name, 420) # create pxe config append information # this information helps to configure the boot server correctly append_filename = ''.join( [ self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.append' ] ) if self.initrd_system == 'kiwi': cmdline = 'pxe=1' else: cmdline = ' '.join( [ 'rd.kiwi.install.pxe', 'rd.kiwi.install.image=http://example.com/image.xz' ] ) custom_cmdline = self.xml_state.build_type.get_kernelcmdline() if custom_cmdline: cmdline += ' ' + custom_cmdline with open(append_filename, 'w') as append: append.write('%s\n' % cmdline) # create initrd for pxe install log.info('Creating pxe install boot image') self._create_pxe_install_kernel_and_initrd() # create pxe install tarball log.info('Creating pxe install archive') archive = ArchiveTar( self.pxename.replace('.xz', '') ) archive.create_xz_compressed( self.pxe_dir, xz_options=self.xz_options ) def _create_pxe_install_kernel_and_initrd(self): kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(self.pxe_dir, '/pxeboot.kernel') os.symlink( 'pxeboot.kernel', ''.join( [ self.pxe_dir, '/', self.xml_state.xml_data.get_name(), '.kernel' ] ) ) else: raise KiwiInstallBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory ) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor(self.pxe_dir, '/pxeboot.xen.gz') else: raise KiwiInstallBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory ) if self.initrd_system == 'dracut': self._create_dracut_install_config() self._add_system_image_boot_options_to_boot_image() self.boot_image_task.create_initrd(self.mbrid, 'initrd_kiwi_install') Command.run( [ 'mv', self.boot_image_task.initrd_filename, self.pxe_dir + '/pxeboot.initrd.xz' ] ) os.chmod(self.pxe_dir + '/pxeboot.initrd.xz', 420) def _create_iso_install_kernel_and_initrd(self): boot_path = self.media_dir + '/boot/' + self.arch + '/loader' Path.create(boot_path) kernel = Kernel(self.boot_image_task.boot_root_directory) if kernel.get_kernel(): kernel.copy_kernel(boot_path, '/linux') else: raise KiwiInstallBootImageError( 'No kernel in boot image tree %s found' % self.boot_image_task.boot_root_directory ) if self.xml_state.is_xen_server(): if kernel.get_xen_hypervisor(): kernel.copy_xen_hypervisor(boot_path, '/xen.gz') else: raise KiwiInstallBootImageError( 'No hypervisor in boot image tree %s found' % self.boot_image_task.boot_root_directory ) if self.initrd_system == 'dracut': self._create_dracut_install_config() self._add_system_image_boot_options_to_boot_image() self.boot_image_task.create_initrd(self.mbrid, 'initrd_kiwi_install') Command.run( [ 'mv', self.boot_image_task.initrd_filename, boot_path + '/initrd' ] ) def _add_system_image_boot_options_to_boot_image(self): filename = ''.join( [self.boot_image_task.boot_root_directory, '/config.bootoptions'] ) self.boot_image_task.include_file( os.sep + os.path.basename(filename) ) def _copy_system_image_initrd_to_iso_image(self): boot_names = self.boot_image_task.get_boot_names() system_image_initrd = os.sep.join( [self.root_dir, 'boot', boot_names.initrd_name] ) shutil.copy( system_image_initrd, self.media_dir + '/initrd.system_image' ) def _write_install_image_info_to_iso_image(self): iso_trigger = self.media_dir + '/config.isoclient' with open(iso_trigger, 'w') as iso_system: iso_system.write('IMAGE="%s"\n' % self.squashed_diskname) def _write_install_image_info_to_boot_image(self): initrd_trigger = \ self.boot_image_task.boot_root_directory + '/config.vmxsystem' with open(initrd_trigger, 'w') as vmx_system: vmx_system.write('IMAGE="%s"\n' % self.squashed_diskname) def _create_dracut_install_config(self): dracut_config = [ 'hostonly="no"', 'dracut_rescue_image="no"' ] dracut_modules = ['kiwi-lib', 'kiwi-dump'] dracut_modules_omit = ['kiwi-overlay', 'kiwi-live', 'kiwi-repart'] if self.root_filesystem_is_multipath is False: dracut_modules_omit.append('multipath') dracut_config.append( 'add_dracutmodules+=" {0} "'.format(' '.join(dracut_modules)) ) dracut_config.append( 'omit_dracutmodules+=" {0} "'.format(' '.join(dracut_modules_omit)) ) with open(self.dracut_config_file, 'w') as config: for entry in dracut_config: config.write(entry + os.linesep) def _delete_dracut_install_config(self): if os.path.exists(self.dracut_config_file): os.remove(self.dracut_config_file) def __del__(self): log.info('Cleaning up %s instance', type(self).__name__) if self.initrd_system == 'dracut': self._delete_dracut_install_config() if self.media_dir: Path.wipe(self.media_dir) if self.pxe_dir: Path.wipe(self.pxe_dir) if self.squashed_contents: Path.wipe(self.squashed_contents)