def main(): """ DistMigration load new kernel for kexec reboot Loads the new kernel/initrd after migration for system reboot """ Logger.setup() log = logging.getLogger(Defaults.get_migration_log_name()) if not MigrationConfig().is_soft_reboot_requested(): log.info('skipping kexec --load (hard reboot requested)') return root_path = Defaults.get_system_root_path() target_kernel = os.sep.join([root_path, Defaults.get_target_kernel()]) target_initrd = os.sep.join([root_path, Defaults.get_target_initrd()]) kexec_boot_data = '/var/tmp/kexec' Path.create(kexec_boot_data) shutil.copy(target_initrd, kexec_boot_data) try: log.info('Running kernel load service') log.info('Loading the target kernel') Command.run([ 'kexec', '--load', target_kernel, '--initrd', os.sep.join([kexec_boot_data, os.path.basename(target_initrd)]), '--kexec-file-syscall', '--command-line', _get_cmdline(os.path.basename(target_kernel)) ]) except Exception as issue: log.error('Kernel load service raised exception: {0}'.format(issue)) raise DistMigrationKernelRebootException( 'Failed to load kernel/initrd into memory: {0}'.format(issue))
def mount_system(root_path, fstab): log = logging.getLogger(Defaults.get_migration_log_name()) log.info('Mount system in {0}'.format(root_path)) system_mount = Fstab() explicit_mount_points = { 'devtmpfs': os.sep.join([root_path, 'dev']), 'proc': os.sep.join([root_path, 'proc']), 'sysfs': os.sep.join([root_path, 'sys']) } try: for fstab_entry in fstab.get_devices(): mountpoint = ''.join([root_path, fstab_entry.mountpoint]) if mountpoint not in explicit_mount_points.values(): if fstab_entry.eligible_for_mount: log.info('Mounting {0}'.format(mountpoint)) Command.run([ 'mount', '-o', fstab_entry.options, fstab_entry.device, mountpoint ]) system_mount.add_entry(fstab_entry.device, mountpoint, fstab_entry.fstype, fstab_entry.eligible_for_mount) log.info('Mounting kernel file systems inside {0}'.format(root_path)) for mount_type, mount_point in explicit_mount_points.items(): Command.run(['mount', '-t', mount_type, mount_type, mount_point]) system_mount.add_entry(mount_type, mount_point) except Exception as issue: log.error('Mounting system for upgrade failed with {0}'.format(issue)) raise DistMigrationSystemMountException( 'Mounting system for upgrade failed with {0}'.format(issue)) system_mount.export(Defaults.get_system_mount_info_file())
def get_root_disk_device(root_path): """ Find unix disk device which is associated with the root mount point given in root_path """ root_device = Command.run( [ 'findmnt', '--first', '--noheadings', '--output', 'SOURCE', '--mountpoint', root_path ] ).output if root_device: lsblk_call = Command.run( [ 'lsblk', '-p', '-n', '-r', '-s', '-o', 'NAME,TYPE', root_device.strip() ] ) considered_block_types = ['disk', 'raid'] for entry in lsblk_call.output.split(os.linesep): block_record = entry.split() if len(block_record) >= 2: block_type = block_record[1] if block_type in considered_block_types: return block_record[0]
def dracut_bind_mounts(root_path,): """Function to do bind mounts needed before running dracut""" log = logging.getLogger(Defaults.get_migration_log_name()) BIND_DIRS = ['/dev', '/proc', '/sys'] for bind_dir in BIND_DIRS: try: log.info( 'Running mount --bind {0} {1}'.format(bind_dir, root_path + bind_dir) ) Command.run( [ 'mount', '--bind', bind_dir, root_path + bind_dir ] ) except Exception as issue: log.error( 'Unable to mount: {0}'.format(issue) ) raise DistMigrationCommandException( 'Unable to mount: {0}'.format( issue ) ) from issue
def main(): """ DistMigration update grub to migrated version Setup and update grub with content from the migrated system Uninstall live migration packages such that they are no longer part of the now migrated system. """ root_path = Defaults.get_system_root_path() grub_config_file = Defaults.get_grub_config_file() try: log.info('Running grub setup service') migration_packages = ['SLE*-Migration', 'suse-migration-*-activation'] log.info('Uninstalling migration: {0}{1}'.format( os.linesep, Command.run([ 'chroot', root_path, 'zypper', '--non-interactive', '--no-gpg-checks', 'remove' ] + migration_packages, raise_on_error=False).output)) log.info('Creating new grub menu: {0}{1}'.format( os.linesep, Command.run([ 'chroot', root_path, 'grub2-mkconfig', '-o', '{0}{1}'.format(os.sep, grub_config_file) ]).error)) except Exception as issue: message = 'Update grub failed with {0}'.format(issue) log.error(message) raise DistMigrationGrubConfigException(message)
def main(): """ DistMigration load new kernel for kexec reboot Loads the new kernel/initrd after migration for system reboot """ root_path = Defaults.get_system_root_path() target_kernel = os.sep.join([root_path, Defaults.get_target_kernel()]) target_initrd = os.sep.join([root_path, Defaults.get_target_initrd()]) kexec_boot_data = '/var/tmp/kexec' Path.create(kexec_boot_data) shutil.copy(target_initrd, kexec_boot_data) try: log.info('Running kernel load service') log.info('Loading the target kernel') Command.run([ 'kexec', '--load', target_kernel, '--initrd', os.sep.join([kexec_boot_data, os.path.basename(target_initrd)]), '--command-line', _get_cmdline(os.path.basename(target_kernel)) ]) except Exception as issue: log.error('Kernel load service raised exception: {0}', format(issue)) raise DistMigrationKernelRebootException( 'Failed to load kernel/initrd into memory: {0}'.format(issue))
def wipe(path): """ Delete path and all contents :param string path: path name """ Command.run( ['rm', '-r', '-f', path] )
def remove(path): """ Delete empty path, causes an error if target is not empty :param string path: path name """ Command.run( ['rmdir', path] )
def create(path): """ Create path and all sub directories to target :param string path: path name """ Command.run( ['mkdir', '-p', path] )
def main(): """ DistMigration ssh access for migration user Copy the authoritation key found to the migration user directory in order to access through ssh """ ssh_keys_glob_paths = Defaults.get_ssh_keys_paths() migration_ssh_file = Defaults.get_migration_ssh_file() system_ssh_host_keys_glob_path = \ Defaults.get_system_ssh_host_keys_glob_path() sshd_config_path = Defaults.get_system_sshd_config_path() try: log.info('Running ssh keys service') ssh_keys_paths = [] for glob_path in ssh_keys_glob_paths: ssh_keys_paths.extend(glob.glob(glob_path)) keys_list = [] for ssh_keys_path in ssh_keys_paths: log.info('Getting keys from {0}'.format(ssh_keys_path)) with open(ssh_keys_path) as authorized_keys_file: keys_list.append(authorized_keys_file.read()) authorized_keys_content = ''.join(keys_list) log.info('Save keys to {0}'.format(migration_ssh_file)) with open(migration_ssh_file, 'w') as authorized_migration_file: authorized_migration_file.write(authorized_keys_content) system_ssh_host_keys = glob.glob(system_ssh_host_keys_glob_path) sshd_config_host_keys_entries = [] log.info('Copying host ssh keys') for system_ssh_host_key in system_ssh_host_keys: if not system_ssh_host_key.endswith('ssh_host_key'): shutil.copy(system_ssh_host_key, '/etc/ssh/') if not system_ssh_host_key.endswith('.pub'): live_private_ssh_host_key_path = os.sep.join([ os.path.dirname(sshd_config_path), os.path.basename(system_ssh_host_key) ]) entry = 'HostKey {0}'.format( live_private_ssh_host_key_path) sshd_config_host_keys_entries.append(entry) with open(sshd_config_path, 'a') as live_sshd_config_file: # write one newline to be sure any subsequent # HostKey entry starts correctly live_sshd_config_file.write(os.linesep) live_sshd_config_file.write( os.linesep.join(sshd_config_host_keys_entries)) log.info('Restarting sshd') Command.run(['systemctl', 'restart', 'sshd']) except Exception as issue: log.error('SSH key/identity setup failed with: {0}. {1}'.format( issue, 'Continue without ssh access'))
def test_run_raises_error(self, mock_popen, mock_which): mock_which.return_value = 'command' mock_process = Mock() mock_process.communicate = Mock( return_value=[str.encode('stdout'), str.encode('stderr')]) mock_process.returncode = 1 mock_popen.return_value = mock_process with raises(DistMigrationCommandException): Command.run(['command', 'args'])
def backup_products_metadata(self): """ Back up the products information. In case migration fails and the migration rolls back to the previous state. The rollback restores this info back in place. """ log.info('Creating backup of Product data') Command.run([ 'rsync', '-zav', '--delete', self.products_metadata + os.sep, '/tmp/products.d.backup/' ])
def main(): """ DistMigration post mount actions Preserve custom data file(s) e.g udev rules from the system to be migrated to the live migration system and activate those file changes to become effective """ Logger.setup() log = logging.getLogger(Defaults.get_migration_log_name()) root_path = Defaults.get_system_root_path() migration_config = MigrationConfig() preserve_info = migration_config.get_preserve_info() if preserve_info: for _, preserve_files in preserve_info.items(): for preserve_file in preserve_files: target_dir = os.path.dirname(preserve_file) source_file = os.path.normpath( os.sep.join([root_path, preserve_file])) log.info('Copy file: {0} to: {1}'.format( source_file, target_dir)) if not os.path.exists(target_dir): Command.run(['mkdir', '-p', target_dir]) shutil.copy(source_file, target_dir) if 'rules' in preserve_info.keys(): Command.run(['udevadm', 'control', '--reload']) Command.run( ['udevadm', 'trigger', '--type=subsystems', '--action=add']) Command.run( ['udevadm', 'trigger', '--type=devices', '--action=add'])
def mount_system(root_path, fstab): log = logging.getLogger(Defaults.get_migration_log_name()) log.info('Mount system in {0}'.format(root_path)) system_mount = Fstab() try: for fstab_entry in fstab.get_devices(): mountpoint = ''.join([root_path, fstab_entry.mountpoint]) log.info('Mounting {0}'.format(mountpoint)) Command.run([ 'mount', '-o', fstab_entry.options, fstab_entry.device, mountpoint ]) system_mount.add_entry(fstab_entry.device, mountpoint, fstab_entry.fstype) log.info('Mounting kernel file systems inside {0}'.format(root_path)) dev_mount_point = os.sep.join([root_path, 'dev']) Command.run(['mount', '-t', 'devtmpfs', 'devtmpfs', dev_mount_point]) system_mount.add_entry('devtmpfs', dev_mount_point) proc_mount_point = os.sep.join([root_path, 'proc']) Command.run(['mount', '-t', 'proc', 'proc', proc_mount_point]) system_mount.add_entry('/proc', proc_mount_point) sys_mount_point = os.sep.join([root_path, 'sys']) Command.run(['mount', '-t', 'sysfs', 'sysfs', sys_mount_point]) system_mount.add_entry('sysfs', sys_mount_point) except Exception as issue: log.error('Mounting system for upgrade failed with {0}'.format(issue)) raise DistMigrationSystemMountException( 'Mounting system for upgrade failed with {0}'.format(issue)) system_mount.export(Defaults.get_system_mount_info_file())
def main(): """ DistMigration run zypper based migration Call zypper migration plugin and migrate the system. The output of the call is logged on the system to migrate """ root_path = Defaults.get_system_root_path() try: log.info('Running migrate service') migration_config = MigrationConfig() if migration_config.is_zypper_migration_plugin_requested(): bash_command = ' '.join([ 'zypper', 'migration', '--non-interactive', '--gpg-auto-import-keys', '--no-selfupdate', '--auto-agree-with-licenses', '--allow-vendor-change', '--strict-errors-dist-migration', '--replacefiles', '--product', migration_config.get_migration_product(), '--root', root_path, '&>>', Defaults.get_migration_log_file() ]) Command.run(['bash', '-c', bash_command]) else: bash_command = ' '.join([ 'zypper', '--non-interactive', '--gpg-auto-import-keys', '--root', root_path, 'dup', '--auto-agree-with-licenses', '--allow-vendor-change', '--replacefiles', '&>>', Defaults.get_migration_log_file() ]) zypper_call = Command.run(['bash', '-c', bash_command], raise_on_error=False) if zypper_has_failed(zypper_call.returncode): raise DistMigrationCommandException( '{0} failed with: {1}: {2}'.format(bash_command, zypper_call.output, zypper_call.error)) except Exception as issue: etc_issue_path = os.sep.join([root_path, 'etc/issue']) log_path_migrated_system = os.sep + os.path.relpath( Defaults.get_migration_log_file(), root_path) with open(etc_issue_path, 'w') as issue_file: issue_file.write( 'Migration has failed, for further details see {0}'.format( log_path_migrated_system)) log.error('migrate service failed with {0}'.format(issue)) raise DistMigrationZypperException( 'Migration failed with {0}'.format(issue))
def main(): """ DistMigration mount system to upgrade Searches on all partitions for a fstab file. The first fstab file found is used as the system to upgrade. Filesystems relevant for an upgrade process are read from that fstab in order and mounted such that the system rootfs is available for a zypper based migration process. """ Logger.setup() log = logging.getLogger(Defaults.get_migration_log_name()) root_path = Defaults.get_system_root_path() Path.create(root_path) log.info('Running mount system service') if is_mounted(root_path): # root_path is already a mount point, better not continue # The condition is not handled as an error because the # existing mount point under this service created root_path # is considered to represent the system to upgrade and # not something else. Thus if already mounted, let's use # what is there. return log.info('Mount system service: {0} is mounted'.format(root_path)) # Check if booted via loopback grub isoscan_loop_mount = '/run/initramfs/isoscan' if is_mounted(isoscan_loop_mount): # The system to become migrated was booted via a grub # loopback menuentry. This means the disk is blocked by # that readonly loopback mount and needs to be # remounted for read write access first log.info( 'Mount system service: {0} is mounted'.format(isoscan_loop_mount)) Command.run(['mount', '-o', 'remount,rw', isoscan_loop_mount]) fstab, storage_info = read_system_fstab(root_path) if not fstab: log.error('Could not find system in fstab on {0}'.format(storage_info)) raise DistMigrationSystemNotFoundException( 'Could not find system with fstab on {0}'.format(storage_info)) mount_system(root_path, fstab) migration_config = MigrationConfig() migration_config.update_migration_config_file() log.info('Config file content:\n{content}\n'.format( content=migration_config.get_migration_config_file_content()))
def main(): """ DistMigration activate host network setup Setup and activate the network as it is setup on the host to become migrated. This includes the import of the resolver and network configuration from the migration host """ Logger.setup() log = logging.getLogger(Defaults.get_migration_log_name()) root_path = Defaults.get_system_root_path() resolv_conf = os.sep.join([root_path, 'etc', 'resolv.conf']) if not os.path.exists(resolv_conf): raise DistMigrationNameResolverException( 'Could not find {0} on migration host'.format(resolv_conf)) if has_host_resolv_setup(resolv_conf): log.info('Copying {}'.format(resolv_conf)) shutil.copy(resolv_conf, '/etc/resolv.conf') else: log.info('Empty {}, continuing without copying it'.format(resolv_conf)) sysconfig_network_providers = os.sep.join( [root_path, 'etc', 'sysconfig', 'network', 'providers']) sysconfig_network_setup = os.sep.join( [root_path, 'etc', 'sysconfig', 'network', '*']) try: log.info('Running setup host network service') system_mount = Fstab() system_mount.read(Defaults.get_system_mount_info_file()) Command.run([ 'mount', '--bind', sysconfig_network_providers, '/etc/sysconfig/network/providers' ]) system_mount.add_entry(sysconfig_network_providers, '/etc/sysconfig/network/providers') for network_setup in glob.glob(sysconfig_network_setup): if os.path.isfile(network_setup): shutil.copy(network_setup, '/etc/sysconfig/network') Command.run(['systemctl', 'reload', 'network']) system_mount.export(Defaults.get_system_mount_info_file()) except Exception as issue: log.error( 'Preparation of migration host network failed with {0}'.format( issue)) raise DistMigrationHostNetworkException( 'Preparation of migration host network failed with {0}'.format( issue))
def test_run_does_not_raise_error(self, mock_popen, mock_which): mock_which.return_value = 'command' mock_process = Mock() mock_process.communicate = Mock( return_value=[str.encode('stdout'), str.encode('')]) mock_process.returncode = 1 mock_popen.return_value = mock_process result = Command.run(['command', 'args'], os.environ, False) assert result.error == '(no output on stderr)' assert result.output == 'stdout' mock_process.communicate = Mock( return_value=[str.encode(''), str.encode('stderr')]) result = Command.run(['command', 'args'], os.environ, False) assert result.error == 'stderr' assert result.output == '(no output on stdout)'
def is_mounted(mount_point): log.info('Checking {0} is mounted'.format(mount_point)) if os.path.exists(mount_point): mountpoint_call = Command.run(['mountpoint', '-q', mount_point], raise_on_error=False) if mountpoint_call.returncode == 0: return True return False
def read_system_fstab(root_path): log = logging.getLogger(Defaults.get_migration_log_name()) log.info('Reading fstab from associated disks') lsblk_call = Command.run(['lsblk', '-p', '-n', '-r', '-o', 'NAME,TYPE']) considered_block_types = ['part', 'raid', 'lvm'] considered_block_devices = [] lvm_managed_block_device_found = False for entry in lsblk_call.output.split(os.linesep): block_record = entry.split() if len(block_record) >= 2: block_type = block_record[1] if block_type in considered_block_types: if block_type == 'lvm': lvm_managed_block_device_found = True considered_block_devices.append(block_record[0]) if lvm_managed_block_device_found: log.info('LVM managed block device(s) found, activating volume groups') Command.run(['vgchange', '-a', 'y']) for block_device in considered_block_devices: try: Command.run(['mount', block_device, root_path], raise_on_error=False) fstab_file = os.sep.join([root_path, 'etc', 'fstab']) if os.path.exists(fstab_file): fstab = Fstab() fstab.read(fstab_file) return (fstab, lsblk_call.output) finally: log.info('Umount {0}'.format(root_path)) Command.run(['umount', root_path], raise_on_error=False) return (None, lsblk_call.output)
def is_mounted(mount_point): log = logging.getLogger(Defaults.get_migration_log_name()) log.info('Checking {0} is mounted'.format(mount_point)) if os.path.exists(mount_point): mountpoint_call = Command.run(['mountpoint', '-q', mount_point], raise_on_error=False) if mountpoint_call.returncode == 0: return True return False
def read_system_fstab(root_path): log.info('Reading fstab from associated disks') lsblk_call = Command.run(['lsblk', '-p', '-n', '-r', '-o', 'NAME,TYPE']) for entry in lsblk_call.output.split(os.linesep): block_record = entry.split() if len(block_record) >= 2: block_type = block_record[1] if block_type == 'part' or block_type.startswith('raid'): try: Command.run(['mount', block_record[0], root_path], raise_on_error=False) fstab_file = os.sep.join([root_path, 'etc', 'fstab']) if os.path.exists(fstab_file): fstab = Fstab() fstab.read(fstab_file) return (fstab, lsblk_call.output) finally: log.info('Umount {0}'.format(root_path)) Command.run(['umount', root_path], raise_on_error=False) return (None, lsblk_call.output)
def test_call(self, mock_select, mock_popen, mock_which): mock_which.return_value = 'command' mock_select.return_value = [True, False, False] mock_process = Mock() mock_popen.return_value = mock_process call = Command.call(['command', 'args']) assert call.output_available() assert call.error_available() assert call.output == mock_process.stdout assert call.error == mock_process.stderr assert call.process == mock_process
def log_network_details(): """ Provide detailed information about the current network setup The method must be called in an active and online network state to provide most useful information about the network interfaces and its setup. """ log.info('All Network Interfaces {0}{1}'.format( os.linesep, Command.run(['ip', 'a'], raise_on_error=False).output)) log.info('Routing Tables {0}{1}'.format( os.linesep, Command.run(['ip', 'r'], raise_on_error=False).output)) log.info('DNS Resolver {0}{1}'.format( os.linesep, Command.run(['cat', '/etc/resolv.conf'], raise_on_error=False).output)) bonding_paths = '/proc/net/bonding/bond*' if os.path.exists(os.path.dirname(bonding_paths)): log.info('Network Bonding {0}{1}'.format( os.linesep, Command.run(['cat', bonding_paths], raise_on_error=False).output))
def test_run(self, mock_popen, mock_exists, mock_access): mock_exists.return_value = True command_run = namedtuple('command', ['output', 'error', 'returncode']) run_result = command_run(output='stdout', error='stderr', returncode=0) mock_process = Mock() mock_process.communicate = Mock( return_value=[str.encode('stdout'), str.encode('stderr')]) mock_process.returncode = 0 mock_popen.return_value = mock_process mock_access.return_value = True assert Command.run(['command', 'args']) == run_result
def main(): """ DistMigration post mount actions Preserve custom data file(s) e.g udev rules from the system to be migrated to the live migration system and activate those file changes to become effective """ Logger.setup() log = logging.getLogger(Defaults.get_migration_log_name()) root_path = Defaults.get_system_root_path() migration_config = MigrationConfig() system_udev_rules = migration_config.get_preserve_udev_rules_list() if system_udev_rules: for rule_file in system_udev_rules: target_rule_dir = os.path.dirname(rule_file) source_rule_file = os.path.normpath( os.sep.join([root_path, rule_file])) log.info('Copy udev rule: {0} to: {1}'.format( source_rule_file, target_rule_dir)) shutil.copy(source_rule_file, target_rule_dir) Command.run(['udevadm', 'control', '--reload']) Command.run( ['udevadm', 'trigger', '--type=subsystems', '--action=add']) Command.run(['udevadm', 'trigger', '--type=devices', '--action=add'])
def mount_system(root_path, fstab): log.info('Mount system in {0}'.format(root_path)) mount_list = [] system_mount = Fstab() for fstab_entry in fstab.get_devices(): try: mountpoint = ''.join([root_path, fstab_entry.mountpoint]) log.info('Mounting {0}'.format(mountpoint)) Command.run([ 'mount', '-o', fstab_entry.options, fstab_entry.device, mountpoint ]) system_mount.add_entry(fstab_entry.device, mountpoint, fstab_entry.fstype) mount_list.append(mountpoint) except Exception as issue: log.error( 'Mounting system for upgrade failed with {0}'.format(issue)) for mountpoint in reversed(mount_list): Command.run(['umount', mountpoint]) raise DistMigrationSystemMountException( 'Mounting system for upgrade failed with {0}'.format(issue)) system_mount.export(Defaults.get_system_mount_info_file())
def main(): """ DistMigration reboot with new kernel After the migration process is finished, the system reboots unless the debug option is set. Before reboot a reverse umount of the filesystems that got mounted by the mount_system service is performed and thus releases the upgraded system from the migration host. If for whatever reason a filesystem is busy and can't be umounted, this condition is not handled as an error. The reason is that the cleanup should not prevent us from continuing with the reboot process. The risk on reboot of the migration host with a potential active mount is something we accept """ Logger.setup() log = logging.getLogger(Defaults.get_migration_log_name()) try: log.info('Systemctl Status Information: {0}{1}'.format( os.linesep, Command.run(['systemctl', 'status', '-l', '--all'], raise_on_error=False).output)) # stop console dialog log. The service holds a busy state # on system-root and stands in our way in case of debug # mode because it grabs the master console in/output Command.run(['systemctl', 'stop', 'suse-migration-console-log'], raise_on_error=False) if MigrationConfig().is_debug_requested(): log.info('Reboot skipped due to debug flag set') else: log.info('Umounting system') system_mount = Fstab() system_mount.read(Defaults.get_system_mount_info_file()) for mount in reversed(system_mount.get_devices()): log.info('Umounting {0}: {1}'.format( mount.mountpoint, Command.run(['umount', '--lazy', mount.mountpoint], raise_on_error=False))) if not MigrationConfig().is_soft_reboot_requested(): restart_system = 'reboot' else: restart_system = 'kexec' log.info('Reboot system: {0}{1}'.format( os.linesep, Command.run(['systemctl', restart_system]))) except Exception: # Uhh, we don't want to be here, but we also don't # want to be stuck in the migration live system. # Keep fingers crossed: log.warning('Reboot system: [Force Reboot]') Command.run(['systemctl', '--force', 'reboot'])
def encryption(): log = logging.getLogger(Defaults.get_migration_log_name()) fstab = Fstab() fstab.read('/etc/fstab') fstab_entries = fstab.get_devices() for fstab_entry in fstab_entries: result = Command.run( ["blkid", "-s", "TYPE", "-o", "value", fstab_entry.device] ) if result.returncode == 0: if 'LUKS' in result.output: log.warning( 'There are encrypted filesystems: {}, this may be an ' 'issue when migrating'.format(fstab_entry.device) )
def is_registered(): """ Run SUSEConnect to list available extensions and modules. If that list exists the system is registered. If this information cannot be provided for some reason, the system is considered unregistered """ root_path = Defaults.get_system_root_path() extensions_cmd_result = Command.run( ['chroot', root_path, 'SUSEConnect', '--list-extensions'], raise_on_error=False) result = True if extensions_cmd_result.returncode != 0: log.error(extensions_cmd_result.output) result = False return result