def test_has_option_in_help_command_failure_exception(self, mock_run): def side_effect(): raise Exception("Something went wrong") mock_run.side_effect = side_effect CommandCapabilities.has_option_in_help('command_that_fails', '--non-existing-flag')
def test_has_option_in_help(self, mock_run): command_type = namedtuple('command', ['output', 'error']) mock_run.return_value = command_type( output="Dummy line\n\t--some-flag\n\t--some-other-flag", error="Dummy line\n\t--error-flag\n\t--some-other-flag" ) assert CommandCapabilities.has_option_in_help('command', '--some-flag') assert CommandCapabilities.has_option_in_help('command', '--error-flag') assert CommandCapabilities.has_option_in_help( 'command', '--some-flag', help_flags=['subcommand', '-h'] ) assert CommandCapabilities.has_option_in_help( 'command', '--some-other-flag', help_flags=['subcommand', '-h'], root='root_dir' ) assert not CommandCapabilities.has_option_in_help( 'command', '--non-existing-flag' ) mock_run.assert_has_calls([ call(['command', '--help']), call(['command', '--help']), call(['command', 'subcommand', '-h']), call(['chroot', 'root_dir', 'command', 'subcommand', '-h']), call(['command', '--help']) ])
def test_has_option_in_help_command_failure_exception(self, mock_run): def side_effect(): raise Exception("Something went wrong") mock_run.side_effect = side_effect CommandCapabilities.has_option_in_help( 'command_that_fails', '--non-existing-flag' )
def test_has_option_in_help_command_failure_warning(self, mock_run): def side_effect(): raise Exception("Something went wrong") mock_run.side_effect = side_effect with self._caplog.at_level(logging.WARNING): CommandCapabilities.has_option_in_help('command_that_fails', '--non-existing-flag', raise_on_error=False)
def test_has_option_in_help_command_failure_warning( self, mock_warn, mock_run): def side_effect(): raise Exception("Something went wrong") mock_run.side_effect = side_effect CommandCapabilities.has_option_in_help('command_that_fails', '--non-existing-flag', raise_on_error=False) assert mock_warn.called
def test_has_option_in_help_command_failure_warning( self, mock_warn, mock_run ): def side_effect(): raise Exception("Something went wrong") mock_run.side_effect = side_effect CommandCapabilities.has_option_in_help( 'command_that_fails', '--non-existing-flag', raise_on_error=False ) assert mock_warn.called
def setup_keyboard_map(self): """ Setup console keyboard """ if 'keytable' in self.preferences: log.info( 'Setting up keytable: %s', self.preferences['keytable'] ) if CommandCapabilities.has_option_in_help( 'systemd-firstboot', '--keymap', root=self.root_dir, raise_on_error=False ): Path.wipe(self.root_dir + '/etc/vconsole.conf') Command.run([ 'chroot', self.root_dir, 'systemd-firstboot', '--keymap=' + self.preferences['keytable'] ]) elif os.path.exists(self.root_dir + '/etc/sysconfig/keyboard'): Shell.run_common_function( 'baseUpdateSysConfig', [ self.root_dir + '/etc/sysconfig/keyboard', 'KEYTABLE', '"' + self.preferences['keytable'] + '"' ] ) else: log.warning( 'keyboard setup skipped no capable ' 'systemd-firstboot or etc/sysconfig/keyboard found' )
def add_efi_loader_parameters(self): """ Add ISO creation parameters to embed the EFI loader In order to boot the ISO from EFI, the EFI binary is added as alternative loader to the ISO creation parameter list. The EFI binary must be included into a fat filesystem in order to become recognized by the firmware. For details about this file refer to _create_embedded_fat_efi_image() from bootloader/config/grub2.py """ loader_file = self.boot_path + '/efi' if os.path.exists(os.sep.join([self.source_dir, loader_file])): self.iso_loaders.append('-eltorito-alt-boot') iso_tool = self.get_tool_name() if iso_tool and CommandCapabilities.has_option_in_help( iso_tool, '-eltorito-platform', raise_on_error=False ): self.iso_loaders += ['-eltorito-platform', 'efi'] self.iso_loaders += [ '-b', loader_file, '-no-emul-boot', '-joliet-long' ] loader_file_512_byte_blocks = os.path.getsize( os.sep.join([self.source_dir, loader_file]) ) / 512 # boot-load-size is stored in a 16bit range, thus we only # set the value if it fits into that range if loader_file_512_byte_blocks <= 0xffff: self.iso_loaders.append( '-boot-load-size' ) self.iso_loaders.append( format(int(loader_file_512_byte_blocks)) )
def setup_keyboard_map(self): """ Setup console keyboard """ if 'keytable' in self.preferences: log.info('Setting up keytable: {0}'.format( self.preferences['keytable'])) if CommandCapabilities.has_option_in_help('systemd-firstboot', '--keymap', root=self.root_dir, raise_on_error=False): Path.wipe(self.root_dir + '/etc/vconsole.conf') Command.run([ 'chroot', self.root_dir, 'systemd-firstboot', '--keymap=' + self.preferences['keytable'] ]) elif os.path.exists(self.root_dir + '/etc/sysconfig/keyboard'): Shell.run_common_function('baseUpdateSysConfig', [ self.root_dir + '/etc/sysconfig/keyboard', 'KEYTABLE', '"' + self.preferences['keytable'] + '"' ]) else: log.warning( 'keyboard setup skipped no capable ' 'systemd-firstboot or etc/sysconfig/keyboard found')
def setup_locale(self): """ Setup UTF8 system wide locale """ if 'locale' in self.preferences: if 'POSIX' in self.preferences['locale'].split(','): locale = 'POSIX' else: locale = '{0}.UTF-8'.format( self.preferences['locale'].split(',')[0]) log.info('Setting up locale: %s', self.preferences['locale']) if CommandCapabilities.has_option_in_help('systemd-firstboot', '--locale', root=self.root_dir, raise_on_error=False): Path.wipe(self.root_dir + '/etc/locale.conf') Command.run([ 'chroot', self.root_dir, 'systemd-firstboot', '--locale=' + locale ]) if os.path.exists(self.root_dir + '/etc/sysconfig/language'): Shell.run_common_function('baseUpdateSysConfig', [ self.root_dir + '/etc/sysconfig/language', 'RC_LANG', locale ])
def setup_locale(self): """ Setup UTF8 system wide locale """ if 'locale' in self.preferences: if 'POSIX' in self.preferences['locale'].split(','): locale = 'POSIX' else: locale = '{0}.UTF-8'.format( self.preferences['locale'].split(',')[0] ) log.info('Setting up locale: %s', self.preferences['locale']) if CommandCapabilities.has_option_in_help( 'systemd-firstboot', '--locale', root=self.root_dir, raise_on_error=False ): Path.wipe(self.root_dir + '/etc/locale.conf') Command.run([ 'chroot', self.root_dir, 'systemd-firstboot', '--locale=' + locale ]) elif os.path.exists(self.root_dir + '/etc/sysconfig/language'): Shell.run_common_function( 'baseUpdateSysConfig', [ self.root_dir + '/etc/sysconfig/language', 'RC_LANG', locale ] ) else: log.warning( 'locale setup skipped no capable ' 'systemd-firstboot or etc/sysconfig/language not found' )
def add_efi_loader_parameters(self): """ Add ISO creation parameters to embed the EFI loader In order to boot the ISO from EFI, the EFI binary is added as alternative loader to the ISO creation parameter list. The EFI binary must be included into a fat filesystem in order to become recognized by the firmware. For details about this file refer to _create_embedded_fat_efi_image() from bootloader/config/grub2.py """ loader_file = self.boot_path + '/efi' if os.path.exists(os.sep.join([self.source_dir, loader_file])): self.iso_loaders.append('-eltorito-alt-boot') iso_tool = self.get_tool_name() if iso_tool and CommandCapabilities.has_option_in_help( iso_tool, '-eltorito-platform', raise_on_error=False): self.iso_loaders += ['-eltorito-platform', 'efi'] self.iso_loaders += [ '-b', loader_file, '-no-emul-boot', '-joliet-long' ] loader_file_512_byte_blocks = os.path.getsize( os.sep.join([self.source_dir, loader_file])) / 512 # boot-load-size is stored in a 16bit range, thus we only # set the value if it fits into that range if loader_file_512_byte_blocks <= 0xffff: self.iso_loaders.append('-boot-load-size') self.iso_loaders.append( format(int(loader_file_512_byte_blocks)))
def post_init(self): """ Initializes some umoci parameters and options """ self.container_name = ':'.join( [self.container_dir, self.container_tag]) if CommandCapabilities.has_option_in_help('umoci', '--no-history', ['repack', '--help']): self.no_history_flag = ['--no-history'] else: self.no_history_flag = []
def test_has_option_in_help(self, mock_run): command_type = namedtuple('command', ['output']) mock_run.return_value = command_type( output="Dummy line\n\t--some-flag\n\t--some-other-flag") assert CommandCapabilities.has_option_in_help('command', '--some-flag') assert CommandCapabilities.has_option_in_help( 'command', '--some-flag', help_flags=['subcommand', '-h']) assert CommandCapabilities.has_option_in_help( 'command', '--some-other-flag', help_flags=['subcommand', '-h'], root='root_dir') assert not CommandCapabilities.has_option_in_help( 'command', '--non-existing-flag') mock_run.assert_has_calls([ call(['command', '--help']), call(['command', 'subcommand', '-h']), call(['chroot', 'root_dir', 'command', 'subcommand', '-h']), call(['command', '--help']) ])
def post_init(self): """ Initializes some umoci parameters and options """ self.oci_dir = mkdtemp(prefix='kiwi_oci_dir.') self.container_dir = os.sep.join([self.oci_dir, 'oci_layout']) self.working_image = '{0}:{1}'.format( self.container_dir, Defaults.get_container_base_image_tag()) if CommandCapabilities.has_option_in_help('umoci', '--no-history', ['config', '--help']): self.no_history_flag = ['--no-history'] else: self.no_history_flag = []
def _check_multitag_support(self) -> None: message = dedent('''\n Using additionaltags attribute requires skopeo tool to be capable to handle it, it must include the '--additional-tag' option for copy subcommand (check it running 'skopeo copy --help').\n It is known to be present since v0.1.30 ''') if 'additional_names' in self.xml_state.get_container_config(): if not CommandCapabilities.has_option_in_help( 'skopeo', '--additional-tag', ['copy', '--help'], raise_on_error=False ): raise KiwiRuntimeError(message)
def create_image_format(self): """ Create ova disk format using ovftool from https://www.vmware.com/support/developer/ovf """ # Check for required ovftool ovftool = Path.which(filename='ovftool', access_mode=os.X_OK) if not ovftool: tool_not_found_message = dedent('''\n Required tool {0} not found in PATH on the build host Building OVA images requires VMware's {0} tool which can be installed from the following location https://www.vmware.com/support/developer/ovf ''') raise KiwiCommandNotFound( tool_not_found_message.format(ovftool) ) # Create the vmdk disk image and vmx config super(DiskFormatOva, self).create_image_format() # Convert to ova using ovftool vmx = self.get_target_file_path_for_format('vmx') ova = self.get_target_file_path_for_format('ova') try: os.unlink(ova) except OSError: pass ovftool_options = [] if CommandCapabilities.has_option_in_help( ovftool, '--shaAlgorithm', raise_on_error=False ): ovftool_options.append('--shaAlgorithm=SHA1') Command.run( [ovftool] + ovftool_options + [vmx, ova] ) # ovftool ignores the umask and creates files with 0600 # apply file permission bits set in the vmx file to the # ova file st = os.stat(vmx) os.chmod(ova, stat.S_IMODE(st.st_mode))
def create_image_format(self): """ Create ova disk format using ovftool from https://www.vmware.com/support/developer/ovf """ # Check for required ovftool ovftool = Path.which(filename='ovftool', access_mode=os.X_OK) if not ovftool: tool_not_found_message = dedent('''\n Required tool {0} not found in PATH on the build host Building OVA images requires VMware's {0} tool which can be installed from the following location https://www.vmware.com/support/developer/ovf ''') raise KiwiCommandNotFound( tool_not_found_message.format(ovftool) ) # Create the vmdk disk image and vmx config self.vmdk.create_image_format() # Convert to ova using ovftool vmx = self.get_target_file_path_for_format('vmx') ova = self.get_target_file_path_for_format('ova') try: os.unlink(ova) except OSError: pass ovftool_options = [] if CommandCapabilities.has_option_in_help( ovftool, '--shaAlgorithm', raise_on_error=False ): ovftool_options.append('--shaAlgorithm=SHA1') Command.run( [ovftool] + ovftool_options + [vmx, ova] ) # ovftool ignores the umask and creates files with 0600 # apply file permission bits set in the vmx file to the # ova file st = os.stat(vmx) os.chmod(ova, stat.S_IMODE(st.st_mode))
def setup_timezone(self): """ Setup timezone symlink """ if 'timezone' in self.preferences: log.info('Setting up timezone: %s', self.preferences['timezone']) if CommandCapabilities.has_option_in_help('systemd-firstboot', '--timezone', root=self.root_dir, raise_on_error=False): Path.wipe(self.root_dir + '/etc/localtime') Command.run([ 'chroot', self.root_dir, 'systemd-firstboot', '--timezone=' + self.preferences['timezone'] ]) else: zoneinfo = '/usr/share/zoneinfo/' + self.preferences['timezone'] Command.run([ 'chroot', self.root_dir, 'ln', '-s', '-f', zoneinfo, '/etc/localtime' ])
def setup_locale(self): """ Setup UTF8 system wide locale """ if 'locale' in self.preferences: if 'POSIX' in self.preferences['locale'].split(','): locale = 'POSIX' else: locale = '{0}.UTF-8'.format( self.preferences['locale'].split(',')[0]) log.info('Setting up locale: {0}'.format( self.preferences['locale'])) if CommandCapabilities.has_option_in_help('systemd-firstboot', '--locale', root=self.root_dir, raise_on_error=False): Path.wipe(self.root_dir + '/etc/locale.conf') Command.run([ 'chroot', self.root_dir, 'systemd-firstboot', '--locale=' + locale ])
def setup_timezone(self): """ Setup timezone symlink """ if 'timezone' in self.preferences: log.info( 'Setting up timezone: %s', self.preferences['timezone'] ) if CommandCapabilities.has_option_in_help( 'systemd-firstboot', '--timezone', root=self.root_dir, raise_on_error=False ): Path.wipe(self.root_dir + '/etc/localtime') Command.run([ 'chroot', self.root_dir, 'systemd-firstboot', '--timezone=' + self.preferences['timezone'] ]) else: zoneinfo = '/usr/share/zoneinfo/' + self.preferences['timezone'] Command.run([ 'chroot', self.root_dir, 'ln', '-s', '-f', zoneinfo, '/etc/localtime' ])
def check_luksformat_options_valid(self) -> None: """ Options set via the luksformat element are passed along to the cryptsetup tool. Only options that are known to the tool should be allowed. Thus this runtime check looks up the provided option names if they exist in the cryptsetup version used on the build host """ message = dedent('''\n Option {0!r} not found in cryptsetup The Option {0!r} could not be found in the help output of the cryptsetup tool. ''') luksformat = self.xml_state.build_type.get_luksformat() if luksformat: for option in luksformat[0].get_option(): argument = option.get_name() if not CommandCapabilities.has_option_in_help( 'cryptsetup', argument, ['--help'], raise_on_error=False ): raise KiwiRuntimeError(message.format(argument))
def create_image_format(self): # Creates the vmdk disk image and vmx config super(DiskFormatOva, self).create_image_format() # Converts to ova using ovftool vmx = self.get_target_file_path_for_format('vmx') ova = self.get_target_file_path_for_format('ova') try: os.unlink(ova) except OSError: pass ovftool_cmd = ['ovftool'] if CommandCapabilities.has_option_in_help( 'ovftool', '--shaAlgorithm', raise_on_error=False): ovftool_cmd.append('--shaAlgorithm=SHA1') ovftool_cmd.extend([vmx, ova]) try: Command.run(ovftool_cmd) except KiwiCommandNotFound as e: log.info('Building OVA images requires VMware\'s ovftool, get it from https://www.vmware.com/support/developer/ovf/') raise e # ovftool ignores the umask and creates files with 0600 for some reason st = os.stat(vmx) os.chmod(ova, stat.S_IMODE(st.st_mode))
def create(self, overwrite: bool = True): """ Setup a loop device of the blocksize given in the constructor The file to loop is created with the size specified in the constructor unless an existing one should not be overwritten :param bool overwrite: overwrite existing file to loop """ if overwrite: qemu_img_size = format(self.filesize_mbytes) + 'M' Command.run(['qemu-img', 'create', self.filename, qemu_img_size]) loop_options = [] if self.blocksize_bytes and self.blocksize_bytes != 512: if CommandCapabilities.has_option_in_help('losetup', '--sector-size', raise_on_error=False): loop_options.append('--sector-size') else: loop_options.append('--logical-blocksize') loop_options.append(format(self.blocksize_bytes)) loop_call = Command.run(['losetup'] + loop_options + ['-f', '--show', self.filename]) self.node_name = loop_call.output.rstrip('\n')