diff --git a/etc/leapp/files/devel-livemode.ini b/etc/leapp/files/devel-livemode.ini new file mode 100644 index 0000000000..9220db3ee1 --- /dev/null +++ b/etc/leapp/files/devel-livemode.ini @@ -0,0 +1,8 @@ +# Configuration for the *experimental* livemode feature +# For the full list of configuration options, see models/livemode.py +[livemode] +squashfs_fullpath=/var/lib/leapp/live-upgrade.img +setup_network_manager=no +autostart_upgrade_after_reboot=yes +setup_opensshd_with_auth_keys=no +setup_passwordless_root=no diff --git a/etc/leapp/files/livemode.json b/etc/leapp/files/livemode.json deleted file mode 100644 index b6d0e0d95d..0000000000 --- a/etc/leapp/files/livemode.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "enabled": 1, - "url": "", - "squashfs": "/var/lib/leapp/live-upgrade.img", - "with_cache": 0, - "temp_dir": "/var/lib/leapp/tmp", - "dracut_network": "ip=dhcp", - "nm": 1, - "packages": [], - "autostart": 1, - "authorized_keys": "/root/.ssh/authorized_keys", - "strace": "" -} diff --git a/repos/system_upgrade/common/actors/livemode/livemodeconfig/actor.py b/repos/system_upgrade/common/actors/livemode/livemodeconfig/actor.py index 4156175c0d..c677ca4a8a 100644 --- a/repos/system_upgrade/common/actors/livemode/livemodeconfig/actor.py +++ b/repos/system_upgrade/common/actors/livemode/livemodeconfig/actor.py @@ -1,74 +1,22 @@ -import json import os -import os.path from leapp.actors import Actor -from leapp.exceptions import StopActorExecutionError -from leapp.libraries.common.rpms import has_package -from leapp.libraries.stdlib import api -from leapp.models import InstalledRPM, LiveModeConfigFacts, ModelViolationError +from leapp.libraries.actor import scan_livemode_config as scan_livemode_config_lib +from leapp.models import InstalledRPM, LiveModeConfigFacts from leapp.tags import ExperimentalTag, FactsPhaseTag, IPUWorkflowTag -LEAPP_LIVEMODE_JSON = '/etc/leapp/files/livemode.json' class LiveModeConfig(Actor): """ - Read /etc/leapp/files/livemode.json + Read livemode configuration located at /etc/leapp/files/devel-livemode.ini """ - name = 'live_mode_config' + name = 'live_mode_config_scanner' consumes = (InstalledRPM) produces = (LiveModeConfigFacts) tags = (ExperimentalTag, FactsPhaseTag, IPUWorkflowTag,) - def process(self): - unsupported = os.getenv('LEAPP_UNSUPPORTED', 0) - if unsupported != '1' or not os.path.exists(LEAPP_LIVEMODE_JSON): - return - - api.current_logger().info( - 'Loading livemode config from {}'.format(LEAPP_LIVEMODE_JSON) - ) - - try: - with open(LEAPP_LIVEMODE_JSON) as f: - config = json.load(f) - except ValueError as error: - raise StopActorExecutionError( - 'Cannot parse live mode config', - details={'Problem': str(error)}) - except OSError as error: - api.current_logger().error('Failed to read livemode configuration. Error: %s', error) - raise StopActorExecutionError( - 'Cannot read live mode config', - details={'Problem': 'reading {} failed.'.format(LEAPP_LIVEMODE_JSON)}) - if api.current_actor().configuration.architecture != 'x86_64': - raise StopActorExecutionError( - 'Cannot operate on this architecture.', - details={'Problem': 'Live mode has been attempted on x86_64 only.'}) - - if not has_package(InstalledRPM, 'squashfs-tools'): - raise StopActorExecutionError( - 'Cannot use mksquashfs.', - details={'Problem': 'The squashfs-tools package is mandatory for the live mode.'}) - - try: - api.produce(LiveModeConfigFacts( - enabled=int(config['enabled']), - url=config['url'], - squashfs=config['squashfs'], - with_cache=config['with_cache'], - temp_dir=config['temp_dir'], - dracut_network=config['dracut_network'], - nm=config['nm'], - packages=config['packages'], - autostart=int(config['autostart']), - authorized_keys=config['authorized_keys'], - strace=config['strace']) - ) - except ModelViolationError as error: - raise StopActorExecutionError( - 'Cannot correctly parse {}'.format(LEAPP_LIVEMODE_JSON), - details={'Problem': str(error)}) + def process(self): + scan_livemode_config_lib.scan_config_and_emit_message() diff --git a/repos/system_upgrade/common/actors/livemode/livemodeconfig/libraries/scan_livemode_config.py b/repos/system_upgrade/common/actors/livemode/livemodeconfig/libraries/scan_livemode_config.py new file mode 100644 index 0000000000..d79320021c --- /dev/null +++ b/repos/system_upgrade/common/actors/livemode/livemodeconfig/libraries/scan_livemode_config.py @@ -0,0 +1,104 @@ +import configparser + +from leapp.libraries.common.config import architecture, get_env +from leapp.libraries.common.rpms import has_package +from leapp.exceptions import StopActorExecutionError +from leapp.models import InstalledRPM, LiveModeConfigFacts, ModelViolationError +from leapp.libraries.stdlib import api + + +LIVEMODE_CONFIG_LOCATION = '/etc/leapp/files/devel-livemode.ini' +DEFAULT_SQUASHFS_PATH = '/var/lib/leapp/live-upgrade.img' + + +def scan_config_and_emit_message(): + is_unsupported = get_env('LEAPP_UNSUPPORTED', '0') == '1' + is_livemode_enabled = get_env('LEAPP_DEVEL_ENABLE_LIVE_MODE', '0') == '1' + + if not is_unsupported: + api.current_logger().debug('Will not scan livemode config - the upgrade is not unsuppoted.') + return + + if not is_livemode_enabled: + api.current_logger().debug('Will not scan livemode config - the live mode is not enabled.') + return + + if not architecture.matches_architecture(architecture.ARCH_X86_64): + api.current_logger().debug('Will not scan livemode config - livemode is currently limited to x86_64.') + return + + if not has_package(InstalledRPM, 'squashfs-tools'): + # This feature is not to be used by standard users, so stopping the upgrade and providing + # the developer a speedy feedback is OK. + raise StopActorExecutionError( + 'The \'squashfs-tools\' is not installed', + details={'Problem': 'The \'squashfs-tools\' is required for the live mode.'} + ) + + api.current_logger().info('Loading livemode config from %s', LIVEMODE_CONFIG_LOCATION) + parser = configparser.ConfigParser() + + try: + parser.read(LIVEMODE_CONFIG_LOCATION) + except configparser.ParsingError as error: + api.current_logger().error('Failed to parse live mode configuration due to the following error: %s', error) + + details = 'Failed to read livemode configuration due to the following error: {0}.' + raise StopActorExecutionError( + 'Failed to read livemode configuration', + details={'Problem': details.format(error)} + ) + + livemode_section = 'livemode' + if livemode_section not in parser: + details = 'The configuration is missing the \'[{0}]\' section'.format(livemode_section) + raise StopActorExecutionError( + 'Live mode configuration does not have the required structure', + details={'Problem': details} + ) + + config_kwargs = { + 'enabled': True, + 'url_to_load_squashfs_from': None, + 'squashfs_fullpath': DEFAULT_SQUASHFS_PATH, + 'dracut_network': None, + 'setup_network_manager': False, + 'additional_packages': None, + 'autostart_upgrade_after_reboot': True, + 'setup_opensshd_with_auth_keys': None, + 'setup_passwordless_root': False, + 'capture_upgrade_strace_into': None + } + + config_str_options = ( + 'url_to_load_squashfs_from', + 'squashfs_fullpath', + 'dracut_network', + 'additional_packages', + 'setup_opensshd_with_auth_keys', + 'capture_upgrade_strace_into' + ) + + config_bool_options = ( + 'setup_network_manager', + 'setup_passwordless_root', + 'autostart_upgrade_after_reboot', + ) + + livemode_config = parser[livemode_section] + + for config_option in config_str_options: + if config_option in livemode_config: + config_kwargs[config_option] = livemode_config[config_option] + + for config_option in config_bool_options: + if config_option in livemode_config: + config_kwargs[config_option] = livemode_config.getboolean(config_option) + + try: + config = LiveModeConfigFacts(**config_kwargs) + except ModelViolationError as error: + raise StopActorExecutionError('Failed to parse livemode configuration.', + details={'Problem': str(error)}) + + api.produce(config) diff --git a/repos/system_upgrade/common/models/livemode.py b/repos/system_upgrade/common/models/livemode.py index f29b0b890c..1a9dc974fd 100644 --- a/repos/system_upgrade/common/models/livemode.py +++ b/repos/system_upgrade/common/models/livemode.py @@ -2,84 +2,71 @@ from leapp.topics import BootPrepTopic, SystemInfoTopic, TransactionTopic -class LiveModeConfigFacts(Model): +class LiveModeConfig(Model): topic = SystemInfoTopic - """ - the LiveOS artifact is built if enabled = 1 - default is 0 - """ - enabled = fields.Integer() + enabled = fields.Boolean() + """ True if the live mode is enabled """ setup_passwordless_root = fields.Boolean(default=False) """ Setup passwordless root for the live image used during the upgrade. """ + url_to_load_squashfs_from = fields.Nullable(fields.String()) """ - url pointing to the squashfs image. - default is "" (booting locally) - example: "http://192.168.122.1/live-upgrade.img" - """ - url = fields.Nullable(fields.String()) + Url pointing to the squashfs image. + if not set, the upgrade will boot locally + example: "http://192.168.122.1/live-upgrade.img" """ - squashfs image storage filename (full path) - """ - squashfs = fields.Nullable(fields.String()) - """ - include dnf cache into the image, default is 0 - """ - with_cache = fields.Integer() + squashfs_fullpath = fields.String() + """ Path to where the squashfs image should be stored. """ + dracut_network = fields.Nullable(fields.String()) """ - temporary LiveOS directory - """ - temp_dir = fields.Nullable(fields.String()) + Dracut network arguments. + + Required if the url_to_lead_squashfs_from is set - """ - dracut network arguments, mandatory if url is not "" example1: "ip=dhcp" example2: "ip=192.168.122.146::192.168.122.1:255.255.255.0:foo::none" """ - dracut_network = fields.Nullable(fields.String()) - """ - enable the NetworkManager, default is 0 - """ - nm = fields.Integer() + setup_network_manager = fields.Boolean() + """ Enable the NetworkManager """ - """ - list of extra packages to include in the target userspace - example: ["tcpdump", "trace-cmd"] - """ - packages = fields.List(fields.String()) + additional_packages = fields.List(fields.String()) + """ List of extra packages to include in the target userspace """ - """ - autostart the upgrade upon reboot (or use upgrade.autostart=0 to disable) - default is 1 - """ - autostart = fields.Integer() + autostart_upgrade_after_reboot = fields.Boolean() + """ Autostart the upgrade upon reboot """ + setup_opensshd_with_auth_keys = fields.Nullable(fields.String()) """ - openssh-server will be installed if not null + Setup SSHD using the authorized keys file. + + If empty, SSHD will not be enabled. + example: "/root/.ssh/authorized_keys" """ - authorized_keys = fields.Nullable(fields.String()) + capture_upgrade_strace_into = fields.Nullable(fields.String()) """ - strace the leapp upgrade service to this file ("" to disable) + File into which leapp upgrade service's strace output will be written. + + If empty, leapp will not be run under strace. + example: "/var/lib/leapp/upgrade.strace" """ - strace = fields.Nullable(fields.String()) class LiveModeRequirementsTasks(Model): topic = TransactionTopic + packages = fields.List(fields.String()) """ packages to be installed in the target userspace """ - packages = fields.List(fields.String()) class LiveImagePreparationInfo(Model):