def get_snapshot_device(request, devicetree): """Get the ThinLV snapshot device. :param request: a snapshot request :param devicetree: a device tree to look up devices :return: a model of the ThinLV snapshot """ snap_name = request.name.replace('-', '--') origin = request.origin.replace('-', '--').replace('/', '-') origin_dev = devicetree.get_device_by_name(origin) log.debug("Snapshot: name %s has origin %s", request.name, origin_dev) if origin_dev is None: raise KickstartParseError(_("Snapshot: origin \"%s\" doesn't exists!") % request.origin, lineno=request.lineno) if not origin_dev.is_thin_lv: raise KickstartParseError(_("Snapshot: origin \"%(origin)s\" of snapshot " "\"%(name)s\" is not a valid thin LV device.") % {"origin": request.origin, "name": request.name}, lineno=request.lineno) if devicetree.get_device_by_name("%s-%s" % (origin_dev.vg.name, snap_name)): raise KickstartParseError(_("Snapshot %s already exists.") % request.name, lineno=request.lineno) try: return LVMLogicalVolumeDevice( name=request.name, parents=[origin_dev.pool], seg_type="thin", origin=origin_dev ) except ValueError as e: raise KickstartParseError(str(e), lineno=request.lineno)
def verify_s390_constraints(storage, constraints, report_error, report_warning): """ Verify constraints for s390x. Prevent users from installing on s390x with (a) no /boot volume, (b) the root volume on LVM, (c) the root volume not restricted to a single PV, and (d) LDL DASD disks. NOTE: There is not really a way for users to create a / volume restricted to a single PV. The backend support is there, but there are no UI hook-ups to drive that functionality, but I do not personally care. --dcantrell :param storage: a storage to check :param constraints: a dictionary of constraints :param report_error: a function for error reporting :param report_warning: a function for warning reporting """ if not arch.is_s390(): return root = storage.fsset.root_device if '/boot' not in storage.mountpoints and root: if root.type == 'lvmlv' and not root.single_pv: report_error(_("This platform requires /boot on a dedicated " "partition or logical volume. If you do not " "want a /boot volume, you must place / on a " "dedicated non-LVM partition.")) for disk in storage.disks: if disk.type == "dasd" and blockdev.s390.dasd_is_ldl(disk.name): report_error(_("The LDL DASD disk {name} ({busid}) cannot be used " "for the installation. Please format it.") .format(name="/dev/" + disk.name, busid=disk.busid))
def is_valid_stage2_device(self, device, linux=True, non_linux=False): valid = super().is_valid_stage2_device(device, linux, non_linux) # If the stage2 device is on a raid1, check that the stage1 device is also redundant, # either by also being part of an array or by being a disk (which is expanded # to every disk in the array by install_targets). if self.stage1_device and self.stage2_device and \ self.stage2_device.type == "mdarray" and \ self.stage2_device.level in self.stage2_raid_levels and \ self.stage1_device.type != "mdarray": if not self.stage1_device.is_disk: msg = _("boot loader stage2 device %(stage2dev)s is on a multi-disk array, " "but boot loader stage1 device %(stage1dev)s is not. " "A drive failure in %(stage2dev)s could render the system unbootable.") % \ {"stage1dev": self.stage1_device.name, "stage2dev": self.stage2_device.name} self.warnings.append(msg) elif not self.stage2_device.depends_on(self.stage1_device): msg = _("boot loader stage2 device %(stage2dev)s is on a multi-disk array, " "but boot loader stage1 device %(stage1dev)s is not part of this array. " "The stage1 boot loader will only be installed to a single drive.") % \ {"stage1dev": self.stage1_device.name, "stage2dev": self.stage2_device.name} self.warnings.append(msg) return valid
def validate_mountpoint(mountpoint, used_mountpoints, strict=True): if strict: fake_mountpoints = [] else: fake_mountpoints = ["swap", "biosboot", "prepboot"] if mountpoint in used_mountpoints: return _("That mount point is already in use. Try something else?") elif not mountpoint: return _("Please enter a valid mount point.") elif mountpoint in system_mountpoints: return _("That mount point is invalid. Try something else?") elif (lowerASCII(mountpoint) not in fake_mountpoints and ((len(mountpoint) > 1 and mountpoint.endswith("/")) or not mountpoint.startswith("/") or " " in mountpoint or re.search(r'/\.*/', mountpoint) or re.search(r'/\.+$', mountpoint))): # - does not end with '/' unless mountpoint _is_ '/' # - starts with '/' except for "swap", &c # - does not contain spaces # - does not contain pairs of '/' enclosing zero or more '.' # - does not end with '/' followed by one or more '.' return _("That mount point is invalid. Try something else?") else: return ""
def refresh(self, mountpoint, device, checkbox_text = "", snapshots=False, bootpart = False): """ Show confirmation dialog with the optional checkbox. If the `checkbox_text` for the checkbox is not set then the checkbox will not be showed. :param str mountpoint: Mountpoint for device. :param str device: Name of the device. :param str checkbox_text: Text for checkbox. If nothing set do not display the checkbox. :param bool snapshot: If true warn user he's going to delete snapshots too. """ super().refresh() label = self.builder.get_object("confirmLabel") if checkbox_text: self._optional_checkbox.set_label(checkbox_text) else: self._optional_checkbox.hide() if mountpoint: txt = "%s (%s)" % (mountpoint, device) else: txt = device if bootpart: label_text = _("%s may be a system boot partition! Deleting it may break other operating systems. Are you sure you want to delete it?") % txt elif not snapshots: label_text = _("Are you sure you want to delete all of the data on %s?") % txt else: label_text = _("Are you sure you want to delete all of the data on %s, including snapshots and/or subvolumes?") % txt label.set_text(label_text)
def verify_swap(storage, constraints, report_error, report_warning): """ Verify the existence of swap. :param storage: a storage to check :param constraints: a dictionary of constraints :param report_error: a function for error reporting :param report_warning: a function for warning reporting """ swaps = storage.fsset.swap_devices if not swaps: installed = util.total_memory() required = Size("%s MiB" % (constraints[STORAGE_MIN_RAM] + isys.NO_SWAP_EXTRA_RAM)) if not constraints[STORAGE_SWAP_IS_RECOMMENDED]: if installed < required: report_warning(_("You have not specified a swap partition. " "%(requiredMem)s of memory is recommended to continue " "installation without a swap partition, but you only " "have %(installedMem)s.") % {"requiredMem": required, "installedMem": installed}) else: if installed < required: report_error(_("You have not specified a swap partition. " "%(requiredMem)s of memory is required to continue " "installation without a swap partition, but you only " "have %(installedMem)s.") % {"requiredMem": required, "installedMem": installed}) else: report_warning(_("You have not specified a swap partition. " "Although not strictly required in all cases, " "it will significantly improve performance " "for most installations."))
def _main_loop_handleException(self, dump_info): """ Helper method with one argument only so that it can be registered with run_in_loop to run on idle or called from a handler. :type dump_info: an instance of the meh.DumpInfo class """ ty = dump_info.exc_info.type value = dump_info.exc_info.value if (issubclass(ty, blivet.errors.StorageError) and value.hardware_fault) \ or (issubclass(ty, OSError) and value.errno == errno.EIO): # hardware fault or '[Errno 5] Input/Output error' hw_error_msg = _("The installation was stopped due to what " "seems to be a problem with your hardware. " "The exact error message is:\n\n%s.\n\n " "The installer will now terminate.") % str(value) self.intf.messageWindow(_("Hardware error occurred"), hw_error_msg) sys.exit(0) elif isinstance(value, blivet.errors.UnusableConfigurationError): sys.exit(0) elif isinstance(value, NonInteractiveError): sys.exit(0) else: super().handleException(dump_info) return False
def run_dasdfmt_dialog(self, dasd_formatting): """Do DASD formatting if user agrees.""" # Prepare text of the dialog. text = "" text += _("The following unformatted or LDL DASDs have been " "detected on your system. You can choose to format them " "now with dasdfmt or cancel to leave them unformatted. " "Unformatted DASDs cannot be used during installation.\n\n") text += dasd_formatting.dasds_summary + "\n\n" text += _("Warning: All storage changes made using the installer will " "be lost when you choose to format.\n\nProceed to run dasdfmt?\n") # Run the dialog. question_window = YesNoDialog(text) ScreenHandler.push_screen_modal(question_window) if not question_window.answer: return None print(_("This may take a moment."), flush=True) # Do the DASD formatting. dasd_formatting.report.connect(self._show_dasdfmt_report) dasd_formatting.run(self.storage, self.data) dasd_formatting.report.disconnect(self._show_dasdfmt_report) self.update_disks()
def status(self): kickstart_timezone = self._timezone_module.proxy.Timezone if kickstart_timezone: return _("%s timezone") % kickstart_timezone else: return _("Timezone is not set.")
def initialize(self, actions): for (i, action) in enumerate(actions, start=1): mountpoint = "" if action.type in [ACTION_TYPE_DESTROY, ACTION_TYPE_RESIZE]: typeString = """<span foreground='red'>%s</span>""" % \ escape_markup(action.type_desc.title()) else: typeString = """<span foreground='green'>%s</span>""" % \ escape_markup(action.type_desc.title()) if action.obj == ACTION_OBJECT_FORMAT: mountpoint = getattr(action.device.format, "mountpoint", "") if hasattr(action.device, "description"): desc = _("%(description)s (%(deviceName)s)") % {"deviceName": action.device.name, "description": action.device.description} serial = action.device.serial elif hasattr(action.device, "disk"): desc = _("%(deviceName)s on %(container)s") % {"deviceName": action.device.name, "container": action.device.disk.description} serial = action.device.disk.serial else: desc = action.device.name serial = action.device.serial self._store.append([i, typeString, action.object_type_string, desc, mountpoint, serial])
def on_updown_ampm_clicked(self, *args): self._stop_and_maybe_start_time_updating() if self._amPmLabel.get_text() == _("AM"): self._amPmLabel.set_text(_("PM")) else: self._amPmLabel.set_text(_("AM"))
def show_all(self): super().show_all() from pyanaconda.installation import doInstall, doConfiguration from pyanaconda.threading import threadMgr, AnacondaThread thread_args = (self.storage, self.payload, self.data, self.instclass) threadMgr.add(AnacondaThread(name=THREAD_INSTALL, target=doInstall, args=thread_args)) # This will run until we're all done with the install thread. self._update_progress() threadMgr.add(AnacondaThread(name=THREAD_CONFIGURATION, target=doConfiguration, args=thread_args)) # This will run until we're all done with the configuration thread. self._update_progress() util.ipmi_report(IPMI_FINISHED) if self.instclass.eula_path: # Notify user about the EULA (if any). print(_("Installation complete")) print('') print(_("Use of this product is subject to the license agreement found at:")) print(self.instclass.eula_path) print('') # kickstart install, continue automatically if reboot or shutdown selected if flags.automatedInstall and self.data.reboot.action in [KS_REBOOT, KS_SHUTDOWN]: # Just pretend like we got input, and our input doesn't care # what it gets, it just quits. raise ExitMainLoop()
def refresh(self, args=None): """ Refresh screen. """ self._load_new_devices() NormalTUISpoke.refresh(self, args) self._container = ListColumnContainer(1, columns_width=78, spacing=1) summary = self._summary_text() self.window.add_with_separator(TextWidget(summary)) hostname = _("Host Name: %s\n") % self._network_module.proxy.Hostname self.window.add_with_separator(TextWidget(hostname)) current_hostname = _("Current host name: %s\n") % self._network_module.proxy.GetCurrentHostname() self.window.add_with_separator(TextWidget(current_hostname)) # if we have any errors, display them while len(self.errors) > 0: self.window.add_with_separator(TextWidget(self.errors.pop())) dialog = Dialog(_("Host Name")) self._container.add(TextWidget(_("Set host name")), callback=self._set_hostname_callback, data=dialog) for dev_name in self.supported_devices: text = (_("Configure device %s") % dev_name) self._container.add(TextWidget(text), callback=self._configure_network_interface, data=dev_name) self.window.add_with_separator(self._container)
def _activated_device_msg(self, devname): msg = _("Wired (%(interface_name)s) connected\n") \ % {"interface_name": devname} ipv4config = nm.nm_device_ip_config(devname, version=4) ipv6config = nm.nm_device_ip_config(devname, version=6) if ipv4config and ipv4config[0]: addr_str, prefix, gateway_str = ipv4config[0][0] netmask_str = network.prefix2netmask(prefix) dnss_str = ",".join(ipv4config[1]) else: addr_str = dnss_str = gateway_str = netmask_str = "" msg += _(" IPv4 Address: %(addr)s Netmask: %(netmask)s Gateway: %(gateway)s\n") % \ {"addr": addr_str, "netmask": netmask_str, "gateway": gateway_str} msg += _(" DNS: %s\n") % dnss_str if ipv6config and ipv6config[0]: for ipv6addr in ipv6config[0]: addr_str, prefix, gateway_str = ipv6addr # Do not display link-local addresses if not addr_str.startswith("fe80:"): msg += _(" IPv6 Address: %(addr)s/%(prefix)d\n") % \ {"addr": addr_str, "prefix": prefix} return msg
def verify_root(storage, constraints, report_error, report_warning): """ Verify the root. :param storage: a storage to check :param constraints: a dictionary of constraints :param report_error: a function for error reporting :param report_warning: a function for warning reporting """ root = storage.fsset.root_device if root: if root.size < constraints[STORAGE_MIN_ROOT]: report_warning(_("Your root partition is less than %(size)s " "which is usually too small to install " "%(product)s.") % {'size': constraints[STORAGE_MIN_ROOT], 'product': productName}) else: report_error(_("You have not defined a root partition (/), " "which is required for installation of %s" " to continue.") % (productName,)) if storage.root_device and storage.root_device.format.exists: e = storage.must_format(storage.root_device) if e: report_error(e)
def _summary_text(self): """Return summary of current timezone & NTP configuration. :returns: current status :rtype: str """ msg = "" # timezone kickstart_timezone = self._timezone_module.proxy.Timezone timezone_msg = _("not set") if kickstart_timezone: timezone_msg = kickstart_timezone msg += _("Timezone: %s\n") % timezone_msg # newline section separator msg += "\n" # NTP msg += _("NTP servers:") if self._ntp_servers: for status in format_ntp_status_list(self._ntp_servers): msg += "\n%s" % status else: msg += _("not configured") return msg
def verify_partition_format_sizes(storage, constraints, report_error, report_warning): """ Verify that the size of the device is allowed by the format used. :param storage: a storage to check :param constraints: a dictionary of constraints :param report_error: a function for error reporting :param report_warning: a function for warning reporting """ # storage.mountpoints is a property that returns a new dict each time, so # iterating over it is thread-safe. filesystems = storage.mountpoints for (mount, device) in filesystems.items(): problem = filesystems[mount].check_size() if problem < 0: report_error(_("Your %(mount)s partition is too small for " "%(format)s formatting (allowable size is " "%(minSize)s to %(maxSize)s)") % {"mount": mount, "format": device.format.name, "minSize": device.min_size, "maxSize": device.max_size}) elif problem > 0: report_error(_("Your %(mount)s partition is too large for " "%(format)s formatting (allowable size is " "%(minSize)s to %(maxSize)s)") % {"mount": mount, "format": device.format.name, "minSize": device.min_size, "maxSize": device.max_size})
def prompt(self, args=None): """ Customize default prompt. """ prompt = NormalTUISpoke.prompt(self, args) prompt.set_message(_("Please select the timezone. Use numbers or type names directly")) # TRANSLATORS: 'b' to go back prompt.add_option(C_('TUI|Spoke Navigation|Time Settings', 'b'), _("back to region list")) return prompt
def refresh(self, args=None): super().refresh(args) self._container = ListColumnContainer(1, columns_width=78, spacing=1) # check if the storage refresh thread is running if threadMgr.get(THREAD_STORAGE_WATCHER): # storage refresh is running - just report it # so that the user can refresh until it is done # TODO: refresh once the thread is done ? message = _(PAYLOAD_STATUS_PROBING_STORAGE) self.window.add_with_separator(TextWidget(message)) # check if there are any mountable devices if self._mountable_devices: for d in self._mountable_devices: self._container.add(TextWidget(d[1]), callback=self._select_mountable_device, data=d[0]) self.window.add_with_separator(self._container) else: message = _("No mountable devices found") self.window.add_with_separator(TextWidget(message))
def prompt(self, args=None): """ Customize default prompt. """ prompt = NormalTUISpoke.prompt(self, args) prompt.set_message(_("Please select language support to install")) # TRANSLATORS: 'b' to go back prompt.add_option(C_("TUI|Spoke Navigation|Language Support", "b"), _("to return to language list")) return prompt
def check(self): """Check configured storage against software selections. When this method is complete (which should be pretty quickly), the following attributes are available for inspection: success -- A simple boolean defining whether there's enough space or not. error_message -- If unsuccessful, an error message describing the situation. This message is suitable for putting in the info bar at the bottom of a Hub. """ free = self._calculate_free_space() needed = self._calculate_needed_space() log.info("fs space: %s needed: %s", free, needed) if free > needed: result = True message = "" else: result = False deficit = self._calculate_deficit(needed) if deficit: message = _("Not enough space in file systems for the current software selection. " "An additional {} is needed.").format(deficit) else: message = _("Not enough space in file systems for the current software selection.") self.success = result self.error_message = message return result
def _find_existing_installations(devicetree): """Find existing GNU/Linux installations on devices from the device tree. :param devicetree: a device tree to find existing installations in :return: roots of all found installations """ if not os.path.exists(util.getTargetPhysicalRoot()): blivet_util.makedirs(util.getTargetPhysicalRoot()) sysroot = util.getSysroot() roots = [] direct_devices = (dev for dev in devicetree.devices if dev.direct) for device in direct_devices: if not device.format.linux_native or not device.format.mountable or \ not device.controllable: continue try: device.setup() except Exception: # pylint: disable=broad-except log_exception_info(log.warning, "setup of %s failed", [device.name]) continue options = device.format.options + ",ro" try: device.format.mount(options=options, mountpoint=sysroot) except Exception: # pylint: disable=broad-except log_exception_info(log.warning, "mount of %s as %s failed", [device.name, device.format.type]) blivet_util.umount(mountpoint=sysroot) continue if not os.access(sysroot + "/etc/fstab", os.R_OK): blivet_util.umount(mountpoint=sysroot) device.teardown() continue try: (architecture, product, version) = get_release_string() except ValueError: name = _("Linux on %s") % device.name else: # I'd like to make this finer grained, but it'd be very difficult # to translate. if not product or not version or not architecture: name = _("Unknown Linux") elif "linux" in product.lower(): name = _("%(product)s %(version)s for %(arch)s") % \ {"product": product, "version": version, "arch": architecture} else: name = _("%(product)s Linux %(version)s for %(arch)s") % \ {"product": product, "version": version, "arch": architecture} (mounts, swaps) = _parse_fstab(devicetree, chroot=sysroot) blivet_util.umount(mountpoint=sysroot) if not mounts and not swaps: # empty /etc/fstab. weird, but I've seen it happen. continue roots.append(Root(mounts=mounts, swaps=swaps, name=name)) return roots
def parse(self, args): tg = super().parse(args) if tg.iface: if not network.wait_for_network_devices([tg.iface]): raise KickstartParseError(lineno=self.lineno, msg=_("Network interface \"%(nic)s\" required by iSCSI \"%(iscsiTarget)s\" target is not up.") % {"nic": tg.iface, "iscsiTarget": tg.target}) mode = blivet.iscsi.iscsi.mode if mode == "none": if tg.iface: network_proxy = NETWORK.get_proxy() activated_ifaces = network_proxy.GetActivatedInterfaces() blivet.iscsi.iscsi.create_interfaces(activated_ifaces) elif ((mode == "bind" and not tg.iface) or (mode == "default" and tg.iface)): raise KickstartParseError(lineno=self.lineno, msg=_("iscsi --iface must be specified (binding used) either for all targets or for none")) try: blivet.iscsi.iscsi.add_target(tg.ipaddr, tg.port, tg.user, tg.password, tg.user_in, tg.password_in, target=tg.target, iface=tg.iface) iscsi_log.info("added iscsi target %s at %s via %s", tg.target, tg.ipaddr, tg.iface) except (IOError, ValueError) as e: raise KickstartParseError(lineno=self.lineno, msg=str(e)) return tg
def _do_check(self): self.clear_errors() StorageCheckHandler.errors = [] StorageCheckHandler.warnings = [] # We can't overwrite the main Storage instance because all the other # spokes have references to it that would get invalidated, but we can # achieve the same effect by updating/replacing a few key attributes. self.storage.devicetree._devices = self._storage_playground.devicetree._devices self.storage.devicetree._actions = self._storage_playground.devicetree._actions self.storage.devicetree._hidden = self._storage_playground.devicetree._hidden self.storage.devicetree.names = self._storage_playground.devicetree.names self.storage.roots = self._storage_playground.roots # set up bootloader and check the configuration try: configure_storage(self.storage, interactive=True) except BootloaderConfigurationError as e: StorageCheckHandler.errors = str(e).split("\n") reset_bootloader(self.storage) StorageCheckHandler.checkStorage(self) if self.errors: self.set_warning(_("Error checking storage configuration. <a href=\"\">Click for details</a> or press Done again to continue.")) elif self.warnings: self.set_warning(_("Warning checking storage configuration. <a href=\"\">Click for details</a> or press Done again to continue.")) # on_info_bar_clicked requires self._error to be set, so set it to the # list of all errors and warnings that storage checking found. self._error = "\n".join(self.errors + self.warnings) return self._error == ""
def parse(self, args): """Parse the command. Do any glob expansion now, since we need to have the real list of disks available before the execute methods run. """ retval = super().parse(args) # Set the default type. if self.type is None: self.type = CLEARPART_TYPE_NONE # Check the disk label. if self.disklabel and self.disklabel not in DiskLabel.get_platform_label_types(): raise KickstartParseError(_("Disklabel \"{}\" given in clearpart command is not " "supported on this platform.").format(self.disklabel), lineno=self.lineno) # Get the disks names to clear. self.drives = get_device_names(self.drives, disks_only=True, lineno=self.lineno, msg=_("Disk \"{}\" given in clearpart command does " "not exist.")) # Get the devices names to clear. self.devices = get_device_names(self.devices, disks_only=False, lineno=self.lineno, msg=_("Device \"{}\" given in clearpart device list " "does not exist.")) return retval
def _update_action_buttons(self, row): obj = PartStoreRow(*row) device = self.storage.devicetree.get_device_by_id(obj.id) # Disks themselves may be editable in certain ways, but they are never # shrinkable. self._preserveButton.set_sensitive(obj.editable) self._shrinkButton.set_sensitive(obj.editable and not device.is_disk) self._deleteButton.set_sensitive(obj.editable) self._resizeSlider.set_visible(False) if not obj.editable: return # If the selected filesystem does not support shrinking, make that # button insensitive. self._shrinkButton.set_sensitive(device.resizable) if device.resizable: self._setup_slider(device, Size(obj.target)) # Then, disable the button for whatever action is currently selected. # It doesn't make a lot of sense to allow clicking that. if obj.action == _(PRESERVE): self._preserveButton.set_sensitive(False) elif obj.action == _(SHRINK): self._shrinkButton.set_sensitive(False) self._resizeSlider.set_visible(True) elif obj.action == _(DELETE): self._deleteButton.set_sensitive(False)
def _activated_device_msg(self, devname): msg = _("Wired (%(interface_name)s) connected\n") \ % {"interface_name": devname} device = self.nm_client.get_device_by_iface(devname) if device: ipv4config = device.get_ip4_config() if ipv4config: addresses = ipv4config.get_addresses() if addresses: a0 = addresses[0] addr_str = a0.get_address() prefix = a0.get_prefix() netmask_str = network.prefix_to_netmask(prefix) gateway_str = ipv4config.get_gateway() or '' dnss_str = ",".join(ipv4config.get_nameservers()) else: addr_str = dnss_str = gateway_str = netmask_str = "" msg += _(" IPv4 Address: %(addr)s Netmask: %(netmask)s Gateway: %(gateway)s\n") % \ {"addr": addr_str, "netmask": netmask_str, "gateway": gateway_str} msg += _(" DNS: %s\n") % dnss_str ipv6config = device.get_ip6_config() if ipv6config: for address in ipv6config.get_addresses(): addr_str = address.get_address() prefix = address.get_prefix() # Do not display link-local addresses if not addr_str.startswith("fe80:"): msg += _(" IPv6 Address: %(addr)s/%(prefix)d\n") % \ {"addr": addr_str, "prefix": prefix} return msg
def download_escrow_certificate(url): """Download the escrow certificate. :param url: an URL of the certificate :return: a content of the certificate """ # Do we need a network connection? if not url.startswith("/") and not url.startswith("file:"): network_proxy = NETWORK.get_proxy() if not network_proxy.Connected: raise KickstartError(_("Escrow certificate %s requires the network.") % url) # Download the certificate. log.info("Downloading an escrow certificate from: %s", url) try: request = util.requests_session().get(url, verify=True) except requests.exceptions.SSLError as e: raise KickstartError(_("SSL error while downloading the escrow certificate:\n\n%s") % e) except requests.exceptions.RequestException as e: raise KickstartError(_("The following error was encountered while downloading the " "escrow certificate:\n\n%s") % e) try: certificate = request.content finally: request.close() return certificate
def connectToView(self): """Attempt to connect to self.vncconnecthost""" maxTries = 10 self.log.info(_("Attempting to connect to vnc client on host %s..."), self.vncconnecthost) if self.vncconnectport != "": hostarg = self.vncconnecthost + ":" + self.vncconnectport else: hostarg = self.vncconnecthost vncconfigcommand = [self.root + "/usr/bin/vncconfig", "-display", ":%s" % constants.X_DISPLAY_NUMBER, "-connect", hostarg] for _i in range(maxTries): vncconfp = util.startProgram(vncconfigcommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE) # vncconfig process err = vncconfp.communicate()[1].decode("utf-8") if err == '': self.log.info(_("Connected!")) return True elif err.startswith("connecting") and err.endswith("failed\n"): self.log.info(_("Will try to connect again in 15 seconds...")) time.sleep(15) continue else: log.critical(err) util.ipmi_abort(scripts=self.anaconda.ksdata.scripts) sys.exit(1) self.log.error(P_("Giving up attempting to connect after %d try!\n", "Giving up attempting to connect after %d tries!\n", maxTries), maxTries) return False
def status(self): if self._error: return _("Error setting up software source") elif not self.ready: return _("Processing...") else: return self._repo_status()
def preInstall(self): """ Get image and loopback mount it. This is called after partitioning is setup, we now have space to grab the image. If it is a network source Download it to sysroot and provide feedback during the download (using urlgrabber callback). If it is a file:// source then use the file directly. """ error = None if self.data.method.url.startswith("file://"): self.image_path = self.data.method.url[7:] else: error = self._preInstall_url_image() if error: exn = PayloadInstallError(str(error)) if errorHandler.cb(exn) == ERROR_RAISE: raise exn # Used to make install progress % look correct self._adj_size = os.stat(self.image_path)[stat.ST_SIZE] if self.data.method.checksum: progressQ.send_message(_("Checking image checksum")) sha256 = hashlib.sha256() with open(self.image_path, "rb") as f: while True: data = f.read(1024 * 1024) if not data: break sha256.update(data) filesum = sha256.hexdigest() log.debug("sha256 of %s is %s", self.data.method.url, filesum) if util.lowerASCII(self.data.method.checksum) != filesum: log.error("%s does not match checksum.", self.data.method.checksum) exn = PayloadInstallError("Checksum of image does not match") if errorHandler.cb(exn) == ERROR_RAISE: raise exn # If this looks like a tarfile, skip trying to mount it if self.is_tarfile: return # Mount the image and check to see if it is a LiveOS/*.img # style squashfs image. If so, move it to IMAGE_DIR and mount the real # root image on INSTALL_TREE rc = blivet.util.mount(self.image_path, INSTALL_TREE, fstype="auto", options="ro") if rc != 0: log.error("mount error (%s) with %s", rc, self.image_path) exn = PayloadInstallError("mount error %s" % rc) if errorHandler.cb(exn) == ERROR_RAISE: raise exn # Nothing more to mount if not os.path.exists(INSTALL_TREE + "/LiveOS"): self._updateKernelVersionList() return # Mount the first .img in the directory on INSTALL_TREE img_files = glob.glob(INSTALL_TREE + "/LiveOS/*.img") if img_files: # move the mount to IMAGE_DIR os.makedirs(IMAGE_DIR, 0o755) # work around inability to move shared filesystems rc = util.execWithRedirect("mount", ["--make-rprivate", "/"]) if rc == 0: rc = util.execWithRedirect("mount", ["--move", INSTALL_TREE, IMAGE_DIR]) if rc != 0: log.error("error %s moving mount", rc) exn = PayloadInstallError("mount error %s" % rc) if errorHandler.cb(exn) == ERROR_RAISE: raise exn img_file = IMAGE_DIR + "/LiveOS/" + os.path.basename( sorted(img_files)[0]) rc = blivet.util.mount(img_file, INSTALL_TREE, fstype="auto", options="ro") if rc != 0: log.error("mount error (%s) with %s", rc, img_file) exn = PayloadInstallError("mount error %s with %s" % (rc, img_file)) if errorHandler.cb(exn) == ERROR_RAISE: raise exn self._updateKernelVersionList() source = os.statvfs(INSTALL_TREE) self.source_size = source.f_frsize * (source.f_blocks - source.f_bfree)
def status(self): return _("testing status...")
def prompt(self, args=None): return Prompt(_("Installation complete. Press %s to quit") % Prompt.ENTER)
def prompt(self, args=None): """ Customize default prompt. """ prompt = NormalTUISpoke.prompt(self, args) prompt.set_message(_("Please select language support to install")) prompt.add_option(PROMPT_BACK_KEY, _(PROMPT_BACK_DESCRIPTION)) return prompt
try: pidfile.create() except pid.PidFileError as e: log.error("Unable to create %s, exiting", pidfile.filename) # If we had a $DISPLAY at start and zenity is available, we may be # running in a live environment and we can display an error dialog. # Otherwise just print an error. if flags.preexisting_x11 and os.access("/usr/bin/zenity", os.X_OK): # The module-level _() calls are ok here because the language may # be set from the live environment in this case, and anaconda's # language setup hasn't happened yet. # pylint: disable=found-_-in-module-class util.execWithRedirect("zenity", ["--error", "--title", _("Unable to create PID file"), "--text", _("Anaconda is unable to create %s because the file" " already exists. Anaconda is already running, or " "a previous instance of anaconda has crashed.") % pidfile.filename]) else: print("%s already exists, exiting" % pidfile.filename) util.ipmi_report(constants.IPMI_FAILED) sys.exit(1) # add our own additional signal handlers signal.signal(signal.SIGHUP, start_debugger) anaconda.opts = opts
def _show_no_network_warning(self): self.set_warning(_("You need to set up networking first if you "\ "want to use NTP"))
def _parse_subscription_json(cls, subscription_json): """Parse the JSON into list of AttachedSubscription instances. The expected JSON is at top level a list of rather complex dictionaries, with each dictionary describing a single subscription that has been attached to the system. :param str subscription_json: JSON describing what subscriptions have been attached :return: list of attached subscriptions :rtype: list of AttachedSubscription instances """ attached_subscriptions = [] try: subscriptions = json.loads(subscription_json) except json.decoder.JSONDecodeError: log.warning("subscription: failed to parse GetPools() JSON output") # empty attached subscription list is better than an installation # ending crash return [] # find the list of subscriptions consumed_subscriptions = subscriptions.get("consumed", []) log.debug("subscription: parsing %d attached subscriptions", len(consumed_subscriptions)) # split the list of subscriptions into separate subscription dictionaries for subscription_info in consumed_subscriptions: attached_subscription = AttachedSubscription() # user visible product name attached_subscription.name = subscription_info.get( "subscription_name", _("product name unknown")) # subscription support level # - this does *not* seem to directly correlate to system purpose SLA attribute attached_subscription.service_level = subscription_info.get( "service_level", _("unknown")) # SKU # - looks like productId == SKU in this JSON output attached_subscription.sku = subscription_info.get( "sku", _("unknown")) # contract number attached_subscription.contract = subscription_info.get( "contract", _("not available")) # subscription start date # - convert the raw date data from JSON to something more readable start_date = subscription_info.get("starts", _("unknown")) attached_subscription.start_date = cls._pretty_date(start_date) # subscription end date # - convert the raw date data from JSON to something more readable end_date = subscription_info.get("ends", _("unknown")) attached_subscription.end_date = cls._pretty_date(end_date) # consumed entitlements # - this seems to correspond to the toplevel "quantity" key, # not to the pool-level "consumed" key for some reason # *or* the pool-level "quantity" key quantity_string = int(subscription_info.get("quantity_used", 1)) attached_subscription.consumed_entitlement_count = quantity_string # add attached subscription to the list attached_subscriptions.append(attached_subscription) # return the list of attached subscriptions return attached_subscriptions
def initialize(self): NormalSpoke.initialize(self) self.initialize_start() # We consider user creation requested if there was at least one user # in the DBus module user list at startup. # We also remember how the user was called so that we can clear it # in a reasonably safe way & if it was cleared. self._user_list = get_user_list(self._users_module, add_default=True) self._user_requested = False self._requested_user_cleared = False # if user has a name, it's an actual user that has been requested, # rather than a default user added by us if self.user.name: self._user_requested = True # gather references to relevant GUI objects # entry fields self._fullname_entry = self.builder.get_object("fullname_entry") self._username_entry = self.builder.get_object("username_entry") self._password_entry = self.builder.get_object("password_entry") self._password_confirmation_entry = self.builder.get_object( "password_confirmation_entry") # check boxes self._admin_checkbox = self.builder.get_object("admin_checkbox") self._password_required_checkbox = self.builder.get_object( "password_required_checkbox") # advanced user configration dialog button self._advanced_button = self.builder.get_object("advanced_button") # password checking status bar & label self._password_bar = self.builder.get_object("password_bar") self._password_label = self.builder.get_object("password_label") # Install the password checks: # - Has a password been specified? # - If a password has been specified and there is data in the confirm box, do they match? # - How strong is the password? # - Does the password contain non-ASCII characters? # Setup the password checker for password checking self._checker = input_checking.PasswordChecker( initial_password_content=self.password, initial_password_confirmation_content=self.password_confirmation, policy=input_checking.get_policy(self.data, "user")) # configure the checker for password checking self.checker.username = self.username self.checker.secret_type = constants.SecretType.PASSWORD # remove any placeholder texts if either password or confirmation field changes content from initial state self.checker.password.changed_from_initial_state.connect( self.remove_placeholder_texts) self.checker.password_confirmation.changed_from_initial_state.connect( self.remove_placeholder_texts) # connect UI updates to check results self.checker.checks_done.connect(self._checks_done) # username and full name checks self._username_check = input_checking.UsernameCheck() self._fullname_check = input_checking.FullnameCheck() # empty username is considered a success so that the user can leave # the spoke without filling it in self._username_check.success_if_username_empty = True # check that the password is not empty self._empty_check = input_checking.PasswordEmptyCheck() # check that the content of the password field & the conformation field are the same self._confirm_check = input_checking.PasswordConfirmationCheck() # check password validity, quality and strength self._validity_check = input_checking.PasswordValidityCheck() # connect UI updates to validity check results self._validity_check.result.password_score_changed.connect( self.set_password_score) self._validity_check.result.status_text_changed.connect( self.set_password_status) # check if the password contains non-ascii characters self._ascii_check = input_checking.PasswordASCIICheck() # Skip the empty and validity password checks if no username is set self._empty_check.skip = True self._validity_check.skip = True # register the individual checks with the checker in proper order # 0) is the username and fullname valid ? # 1) is the password non-empty ? # 2) are both entered passwords the same ? # 3) is the password valid according to the current password checking policy ? # 4) is the password free of non-ASCII characters ? self.checker.add_check(self._username_check) self.checker.add_check(self._fullname_check) self.checker.add_check(self._empty_check) self.checker.add_check(self._confirm_check) self.checker.add_check(self._validity_check) self.checker.add_check(self._ascii_check) self.guesser = {self.username_entry: True} # Configure levels for the password bar self.password_bar.add_offset_value("low", 2) self.password_bar.add_offset_value("medium", 3) self.password_bar.add_offset_value("high", 4) # Modify the GUI based on the kickstart and policy information # This needs to happen after the input checks have been created, since # the Gtk signal handlers use the input check variables. password_set_message = _("The password was set by kickstart.") if self.password_kickstarted: self.password_required = True self.password_entry.set_placeholder_text(password_set_message) self.password_confirmation_entry.set_placeholder_text( password_set_message) elif not self.checker.policy.emptyok: # Policy is that a non-empty password is required self.password_required = True if not self.checker.policy.emptyok: # User isn't allowed to change whether password is required or not self.password_required_checkbox.set_sensitive(False) self._advanced_user_dialog = AdvancedUserDialog(self) self._advanced_user_dialog.initialize() # set the visibility of the password entries set_password_visibility(self.password_entry, False) set_password_visibility(self.password_confirmation_entry, False) # report that we are done self.initialize_done()
def __init__(self, fullscreen=False, decorated=False): """Create a new anaconda main window. :param bool fullscreen: if True, fullscreen the window, if false maximize """ super().__init__() # Remove the title bar, resize controls and other stuff if the window manager # allows it and decorated is set to False. Otherwise, it has no effect. self.set_decorated(decorated) # Hide the titlebar when maximized if the window manager allows it. # This makes anaconda look full-screenish but without covering parts # needed to interact with the window manager, like the GNOME top bar. self.set_hide_titlebar_when_maximized(True) # The Anaconda and Initial Setup windows might sometimes get decorated with # a titlebar which contains the __init__.py header text by default. # As all Anaconda and Initial Setup usually have a very distinct title text # inside the window, the titlebar text is redundant and should be disabled. self.set_title(_(WINDOW_TITLE_TEXT)) # Set the icon used in the taskbar of window managers that have a taskbar # The "anaconda" icon is part of fedora-logos self.set_icon_name("anaconda") # Treat an attempt to close the window the same as hitting quit self.connect("delete-event", self._on_delete_event) # Create a black, 50% opacity pixel that will be scaled to fit the lightbox overlay # The confusing list of unnamed parameters is: # bytes, colorspace (there is no other colorspace), has-alpha, # bits-per-sample (has to be 8), width, height, # rowstride (bytes between row starts, but we only have one row) self._transparent_base = GdkPixbuf.Pixbuf.new_from_bytes( Bytes.new([0, 0, 0, 127]), GdkPixbuf.Colorspace.RGB, True, 8, 1, 1, 1) # Contain everything in an overlay so the window can be overlayed with the transparency # for the lightbox effect self._overlay = Gtk.Overlay() self._overlay_img = None self._overlay.connect("get-child-position", self._on_overlay_get_child_position) self._overlay_depth = 0 # Create a stack and a list of what's been added to the stack # Double the stack transition duration since the default 200ms is too # quick to get the point across self._stack = Gtk.Stack(transition_duration=400) self._stack_contents = set() # Create an accel group for the F12 accelerators added after window transitions self._accel_group = Gtk.AccelGroup() self.add_accel_group(self._accel_group) # Make the window big if fullscreen: self.fullscreen() else: self.maximize() self._overlay.add(self._stack) self.add(self._overlay) self.show_all() self._current_action = None # Help button mnemonics handling self._mnemonic_signal = None # we have a sensible initial value, just in case self._saved_help_button_label = _("Help!") # Apply the initial language attributes self._language = None self.reapply_language() # Keybinder from GI needs to be initialized before use Keybinder.init() Keybinder.bind("<Shift>Print", self._handle_print_screen, []) self._screenshot_index = 0
def run(self): if self.MESSAGE: self.builder.get_object("quit_message").set_label(_(self.MESSAGE)) rc = self.window.run() return rc
def status_message(nm_client): """A short string describing which devices are connected.""" msg = _("Unknown") if not nm_client: msg = _("Status not available") return msg state = nm_client.get_state() if state == NM.State.CONNECTING: msg = _("Connecting...") elif state == NM.State.DISCONNECTING: msg = _("Disconnecting...") else: active_devs = [d for d in get_activated_devices(nm_client) if not is_libvirt_device(d.get_ip_iface() or d.get_iface())] if active_devs: slaves = {} ssids = {} nonslaves = [] # first find slaves and wireless aps for device in active_devs: device_slaves = [] if hasattr(device, 'get_slaves'): device_slaves = [slave_dev.get_iface() for slave_dev in device.get_slaves()] iface = device.get_iface() slaves[iface] = device_slaves if device.get_device_type() == NM.DeviceType.WIFI: ssid = "" ap = device.get_active_access_point() if ap: ssid = ap.get_ssid().get_data().decode() ssids[iface] = ssid all_slaves = set(itertools.chain.from_iterable(slaves.values())) nonslaves = [dev for dev in active_devs if dev.get_iface() not in all_slaves] if len(nonslaves) == 1: device = nonslaves[0] iface = device.get_ip_iface() or device.get_iface() device_type = device.get_device_type() if device_type_is_supported_wired(device_type): msg = _("Wired (%(interface_name)s) connected") \ % {"interface_name": iface} elif device_type == NM.DeviceType.WIFI: msg = _("Wireless connected to %(access_point)s") \ % {"access_point": ssids[iface]} elif device_type == NM.DeviceType.BOND: msg = _("Bond %(interface_name)s (%(list_of_slaves)s) connected") \ % {"interface_name": iface, "list_of_slaves": ",".join(slaves[iface])} elif device_type == NM.DeviceType.TEAM: msg = _("Team %(interface_name)s (%(list_of_slaves)s) connected") \ % {"interface_name": iface, "list_of_slaves": ",".join(slaves[iface])} elif device_type == NM.DeviceType.BRIDGE: msg = _("Bridge %(interface_name)s (%(list_of_slaves)s) connected") \ % {"interface_name": iface, "list_of_slaves": ",".join(slaves[iface])} elif device_type == NM.DeviceType.VLAN: parent = device.get_parent() vlanid = device.get_vlan_id() msg = _("VLAN %(interface_name)s (%(parent_device)s, ID %(vlanid)s) connected") \ % {"interface_name": iface, "parent_device": parent, "vlanid": vlanid} elif len(nonslaves) > 1: devlist = [] for device in nonslaves: iface = device.get_ip_iface() or device.get_iface() device_type = device.get_device_type() if device_type_is_supported_wired(device_type): devlist.append("%s" % iface) elif device_type == NM.DeviceType.WIFI: devlist.append("%s" % ssids[iface]) elif device_type == NM.DeviceType.BOND: devlist.append("%s (%s)" % (iface, ",".join(slaves[iface]))) elif device_type == NM.DeviceType.TEAM: devlist.append("%s (%s)" % (iface, ",".join(slaves[iface]))) elif device_type == NM.DeviceType.BRIDGE: devlist.append("%s (%s)" % (iface, ",".join(slaves[iface]))) elif device_type == NM.DeviceType.VLAN: devlist.append("%s" % iface) msg = _("Connected: %(list_of_interface_names)s") % {"list_of_interface_names": ", ".join(devlist)} else: msg = _("Not connected") if not get_supported_devices(): msg = _("No network devices available") return msg
def _show_no_ntp_server_warning(self): self.set_warning(_("You have no working NTP server configured"))
def _check_nfs_server(self, user_input, report_func): if ":" not in user_input or len(user_input.split(":")) != 2: report_func(_("Server must be specified as SERVER:/PATH")) return False return True
def _noDisksHandler(self, exn): message = _("An error has occurred - no valid devices were found on " "which to create new file systems. Please check your " "hardware for the cause of this problem.") self.ui.showError(message) return ERROR_RAISE
def refresh(self, args=None): NormalTUISpoke.refresh(self, args) msg = _("Please select new root password. You will have to type it twice.") self.window.add_with_separator(TextWidget(msg))
def _passwordCryptErrorHandler(self, exn): message = _( "Unable to encrypt password: unsupported algorithm %s") % exn.algo self.ui.showError(message) return ERROR_RAISE
def status(self): if self._l12_module.Language: return localization.get_english_name(self._selected) else: return _("Language is not set.")
def setup_display(anaconda, options): """Setup the display for the installation environment. :param anaconda: instance of the Anaconda class :param options: command line/boot options """ try: xtimeout = int(options.xtimeout) except ValueError: log.warning("invalid inst.xtimeout option value: %s", options.xtimeout) xtimeout = constants.X_TIMEOUT vnc_server = vnc.VncServer() # The vnc Server object. vnc_server.anaconda = anaconda vnc_server.timeout = xtimeout anaconda.display_mode = options.display_mode anaconda.interactive_mode = not options.noninteractive if options.vnc: flags.usevnc = True if not anaconda.gui_mode: log.info( "VNC requested via boot/CLI option, switching Anaconda to GUI mode." ) anaconda.display_mode = constants.DisplayModes.GUI vnc_server.password = options.vncpassword # Only consider vncconnect when vnc is a param if options.vncconnect: cargs = options.vncconnect.split(":") vnc_server.vncconnecthost = cargs[0] if len(cargs) > 1 and len(cargs[1]) > 0: if len(cargs[1]) > 0: vnc_server.vncconnectport = cargs[1] if options.xdriver: write_xdriver(options.xdriver, root="/") if flags.rescue_mode: return if anaconda.ksdata.vnc.enabled: flags.usevnc = True if not anaconda.gui_mode: log.info( "VNC requested via kickstart, switching Anaconda to GUI mode.") anaconda.display_mode = constants.DisplayModes.GUI if vnc_server.password == "": vnc_server.password = anaconda.ksdata.vnc.password if vnc_server.vncconnecthost == "": vnc_server.vncconnecthost = anaconda.ksdata.vnc.host if vnc_server.vncconnectport == "": vnc_server.vncconnectport = anaconda.ksdata.vnc.port if anaconda.gui_mode: mods = (tup[1] for tup in pkgutil.iter_modules(pyanaconda.ui.__path__, "pyanaconda.ui.")) if "pyanaconda.ui.gui" not in mods: stdout_log.warning( "Graphical user interface not available, falling back to text mode" ) anaconda.display_mode = constants.DisplayModes.TUI flags.usevnc = False flags.vncquestion = False # check if VNC can be started vnc_can_be_started, vnc_error_messages = check_vnc_can_be_started(anaconda) if not vnc_can_be_started: # VNC can't be started - disable the VNC question and log # all the errors that prevented VNC from being started flags.vncquestion = False for error_message in vnc_error_messages: stdout_log.warning(error_message) # Should we try to start Xorg? want_x = anaconda.gui_mode and not (flags.preexisting_x11 or flags.usevnc) # X on a headless (e.g. s390) system? Nonsense! if want_x and blivet.arch.is_s390(): stdout_log.warning( _("Running on a headless system. Starting text mode.")) anaconda.display_mode = constants.DisplayModes.TUI anaconda.gui_startup_failed = True time.sleep(2) want_x = False # Is Xorg is actually available? if want_x and not os.access("/usr/bin/Xorg", os.X_OK): stdout_log.warning( _("Graphical installation is not available. " "Starting text mode.")) time.sleep(2) anaconda.display_mode = constants.DisplayModes.TUI want_x = False if anaconda.tui_mode and flags.vncquestion: # we prefer vnc over text mode, so ask about that message = _("Text mode provides a limited set of installation " "options. It does not offer custom partitioning for " "full control over the disk layout. Would you like " "to use VNC mode instead?") ask_vnc_question(anaconda, vnc_server, message) if not anaconda.ksdata.vnc.enabled: # user has explicitly specified text mode flags.vncquestion = False anaconda.log_display_mode() startup_utils.check_memory(anaconda, options) # check_memory may have changed the display mode want_x = want_x and (anaconda.gui_mode) if want_x: try: start_x11(xtimeout) do_startup_x11_actions() except (OSError, RuntimeError) as e: log.warning("X startup failed: %s", e) stdout_log.warning("X startup failed, falling back to text mode") anaconda.display_mode = constants.DisplayModes.TUI anaconda.gui_startup_failed = True time.sleep(2) if not anaconda.gui_startup_failed: do_extra_x11_actions(options.runres, gui_mode=anaconda.gui_mode) if anaconda.tui_mode and anaconda.gui_startup_failed and flags.vncquestion and not anaconda.ksdata.vnc.enabled: message = _( "X was unable to start on your machine. Would you like to start VNC to connect to " "this computer from another computer and perform a graphical installation or continue " "with a text mode installation?") ask_vnc_question(anaconda, vnc_server, message) # if they want us to use VNC do that now if anaconda.gui_mode and flags.usevnc: vnc_server.startServer() do_startup_x11_actions() # with X running we can initialize the UI interface anaconda.initInterface()
def validate_device_factory_request(storage, request: DeviceFactoryRequest): """Validate the given device info. :param storage: an instance of Blivet :param request: a device factory request to validate :return: an error message """ device = storage.devicetree.resolve_device(request.device_spec) device_type = request.device_type reformat = request.reformat fs_type = request.format_type encrypted = request.device_encrypted raid_level = get_raid_level_by_name(request.device_raid_level) mount_point = request.mount_point label = request.label num_disk = len(request.disks) changed_label = label != getattr(device.format, "label", "") changed_fstype = fs_type != device.format.type if changed_label or changed_fstype: error = validate_label(label, get_format(fs_type)) if error: return error is_format_mountable = get_format(fs_type).mountable changed_mount_point = mount_point != getattr(device.format, "mountpoint", "") if reformat and is_format_mountable and not mount_point: return _("Please enter a mount point.") if changed_mount_point and mount_point: error = validate_mount_point(mount_point, storage.mountpoints.keys()) if error: return error supported_types = (devicefactory.DEVICE_TYPE_PARTITION, devicefactory.DEVICE_TYPE_MD) if mount_point == "/boot/efi" and device_type not in supported_types: return _("/boot/efi must be on a device of type {type} or {another}" ).format(type=_( DEVICE_TEXT_MAP[devicefactory.DEVICE_TYPE_PARTITION]), another=_( DEVICE_TEXT_MAP[devicefactory.DEVICE_TYPE_MD])) if device_type != devicefactory.DEVICE_TYPE_PARTITION and \ fs_type in PARTITION_ONLY_FORMAT_TYPES: return _("{fs} must be on a device of type {type}").format( fs=fs_type, type=_(DEVICE_TEXT_MAP[devicefactory.DEVICE_TYPE_PARTITION])) if mount_point and encrypted and mount_point.startswith("/boot"): return _("{} cannot be encrypted").format(mount_point) if encrypted and fs_type in PARTITION_ONLY_FORMAT_TYPES: return _("{} cannot be encrypted").format(fs_type) if mount_point == "/" and device.format.exists and not reformat: return _("You must create a new file system on the root device.") if (raid_level is not None or device_type == devicefactory.DEVICE_TYPE_MD) and \ raid_level not in get_supported_raid_levels(device_type): return _("Device does not support RAID level selection {}.").format( raid_level) if raid_level is not None: error = validate_raid_level(raid_level, num_disk) if error: return error return None
def is_valid_stage1_device(self, device, early=False): """ Return True if the device is a valid stage1 target device. Also collect lists of errors and warnings. The criteria for being a valid stage1 target device vary from platform to platform. On some platforms a disk with an msdos disklabel is a valid stage1 target, while some platforms require a special device. Some examples of these special devices are EFI system partitions on EFI machines, PReP boot partitions on iSeries, and Apple bootstrap partitions on Mac. The 'early' keyword argument is a boolean flag indicating whether or not this check is being performed at a point where the mountpoint cannot be expected to be set for things like EFI system partitions. """ self.errors = [] self.warnings = [] valid = True constraint = platform.boot_stage1_constraint_dict if device is None: return False log.debug("Is %s a valid stage1 target device?", device.name) if not self._device_type_match(device, constraint["device_types"]): log.debug("stage1 device cannot be of type %s", device.type) return False if _is_on_sw_iscsi(device): if not _is_on_ibft(device): if conf.bootloader.nonibft_iscsi_boot: log.debug("stage1 device on non-iBFT iSCSI disk allowed " "by boot option inst.iscsi.nonibftboot") else: log.debug( "stage1 device cannot be on an non-iBFT iSCSI disk") self.errors.append( _("Boot loader stage1 device cannot be on " "an iSCSI disk which is not configured in iBFT.")) return False description = self.device_description(device) if self.stage2_is_valid_stage1 and device == self.stage2_device: # special case valid = (self.stage2_is_preferred_stage1 and self.is_valid_stage2_device(device)) # we'll be checking stage2 separately so don't duplicate messages self.warnings = [] return valid if device.protected: valid = False if not self._is_valid_disklabel(device, disklabel_types=self.disklabel_types): valid = False if not self._is_valid_size(device, desc=description): valid = False if not self._is_valid_location( device, max_end=constraint["max_end"], desc=description): valid = False if not self._is_valid_md(device, raid_levels=constraint["raid_levels"], metadata=constraint["raid_metadata"], member_types=constraint["raid_member_types"], desc=description): valid = False if not self.stage2_bootable and not getattr(device, "bootable", True): log.warning("%s not bootable", device.name) # XXX does this need to be here? if getattr(device.format, "label", None) in ("ANACONDA", "LIVE"): log.info("ignoring anaconda boot disk") valid = False if early: mountpoints = [] else: mountpoints = constraint["mountpoints"] if not self._is_valid_format(device, format_types=constraint["format_types"], mountpoints=mountpoints, desc=description): valid = False if not self.encryption_support and device.encrypted: self.errors.append( _("%s cannot be on an encrypted block " "device.") % description) valid = False log.debug("is_valid_stage1_device(%s) returning %s", device.name, valid) return valid
def description(self): """Get description of this source.""" return _("Local media via SE/HMC")
def _update_subscription_state(self): """Update state of the subscription related part of the spoke. Update state of the part of the spoke, that shows data about the currently attached subscriptions. """ # authentication method if self.authentication_method == AuthenticationMethod.USERNAME_PASSWORD: method_string = _("Registered with account {}").format( self.subscription_request.account_username) else: # org + key method_string = _("Registered with organization {}").format( self.subscription_request.organization) self._method_status_label.set_text(method_string) # final syspurpose data # role final_role_string = _("Role: {}").format(self.system_purpose_data.role) self._role_status_label.set_text(final_role_string) # SLA final_sla_string = _("SLA: {}").format(self.system_purpose_data.sla) self._sla_status_label.set_text(final_sla_string) # usage final_usage_string = _("Usage: {}").format( self.system_purpose_data.usage) self._usage_status_label.set_text(final_usage_string) # Insights # - this strings are referring to the desired target system state, # the installation environment itself is not expected to be # connected to Insights if self._subscription_module.InsightsEnabled: insights_string = _("Connected to Red Hat Insights") else: insights_string = _("Not connected to Red Hat Insights") self._insights_status_label.set_text(insights_string) # get attached subscriptions as a list of structs attached_subscriptions = self._subscription_module.AttachedSubscriptions # turn the structs to more useful AttachedSubscription instances attached_subscriptions = AttachedSubscription.from_structure_list( attached_subscriptions) # check how many we have & set the subscription status string accordingly subscription_count = len(attached_subscriptions) if subscription_count == 0: subscription_string = _( "No subscriptions are attached to the system") elif subscription_count == 1: subscription_string = _("1 subscription attached to the system") else: subscription_string = _("{} subscriptions attached to the system" ).format(subscription_count) self._attached_subscriptions_label.set_text(subscription_string) # populate the attached subscriptions listbox populate_attached_subscriptions_listbox(self._subscriptions_listbox, attached_subscriptions)
def is_valid_stage2_device(self, device, linux=True, non_linux=False): """ Return True if the device is suitable as a stage2 target device. Also collect lists of errors and warnings. """ self.errors = [] self.warnings = [] valid = True if device is None: return False log.debug("Is %s a valid stage2 target device?", device.name) if device.protected: valid = False if _is_on_sw_iscsi(device): if not _is_on_ibft(device): if conf.bootloader.nonibft_iscsi_boot: log.info( "%s on non-iBFT iSCSI disk allowed by boot option inst.nonibftiscsiboot", self.stage2_description) else: self.errors.append( _("%(bootloader_stage2_description)s cannot be on " "an iSCSI disk which is not configured in iBFT.") % { "bootloader_stage2_description": self.stage2_description }) valid = False if not self._device_type_match(device, self.stage2_device_types): self.errors.append( _("%(desc)s cannot be of type %(type)s") % { "desc": _(self.stage2_description), "type": device.type }) valid = False if not self._is_valid_disklabel(device, disklabel_types=self.disklabel_types): valid = False if not self._is_valid_size(device, desc=_(self.stage2_description)): valid = False if self.stage2_max_end and not self._is_valid_location( device, max_end=self.stage2_max_end, desc=_(self.stage2_description)): valid = False if not self._is_valid_partition(device, primary=self.stage2_must_be_primary): valid = False if not self._is_valid_md(device, raid_levels=self.stage2_raid_levels, metadata=self.stage2_raid_metadata, member_types=self.stage2_raid_member_types, desc=_(self.stage2_description)): valid = False if linux and \ not self._is_valid_format(device, format_types=self.stage2_format_types, mountpoints=self.stage2_mountpoints, desc=_(self.stage2_description)): valid = False non_linux_format_types = platform._non_linux_format_types if non_linux and \ not self._is_valid_format(device, format_types=non_linux_format_types): valid = False if not self.encryption_support and device.encrypted: self.errors.append( _("%s cannot be on an encrypted block " "device.") % _(self.stage2_description)) valid = False log.debug("is_valid_stage2_device(%s) returning %s", device.name, valid) return valid
def preInstall(self): """ Perform pre-installation tasks. """ super().preInstall() progressQ.send_message(_("Installing software") + (" %d%%") % (0, ))
def install(self): mainctx = create_new_context() mainctx.push_thread_default() cancellable = None gi.require_version("OSTree", "1.0") gi.require_version("RpmOstree", "1.0") from gi.repository import OSTree, RpmOstree ostreesetup = self.data.ostreesetup log.info("executing ostreesetup=%r", ostreesetup) # Initialize the filesystem - this will create the repo as well self._safeExecWithRedirect("ostree", ["admin", "--sysroot=" + util.getTargetPhysicalRoot(), "init-fs", util.getTargetPhysicalRoot()]) # Here, we use the physical root as sysroot, because we haven't # yet made a deployment. sysroot_file = Gio.File.new_for_path(util.getTargetPhysicalRoot()) sysroot = OSTree.Sysroot.new(sysroot_file) sysroot.load(cancellable) repo = sysroot.get_repo(None)[1] # We don't support resuming from interrupted installs repo.set_disable_fsync(True) self._remoteOptions = {} if hasattr(ostreesetup, 'nogpg') and ostreesetup.nogpg: self._remoteOptions['gpg-verify'] = Variant('b', False) if flags.noverifyssl: self._remoteOptions['tls-permissive'] = Variant('b', True) repo.remote_change(None, OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS, ostreesetup.remote, ostreesetup.url, Variant('a{sv}', self._remoteOptions), cancellable) # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299 ref = RpmOstree.varsubst_basearch(ostreesetup.ref) progressQ.send_message(_("Starting pull of %(branchName)s from %(source)s") % \ {"branchName": ref, "source": ostreesetup.remote}) progress = OSTree.AsyncProgress.new() progress.connect('changed', self._pullProgressCb) pull_opts = {'refs': Variant('as', [ref])} # If we're doing a kickstart, we can at least use the content as a reference: # See <https://github.com/rhinstaller/anaconda/issues/1117> # The first path here is used by <https://pagure.io/fedora-lorax-templates> # and the second by <https://github.com/projectatomic/rpm-ostree-toolbox/> if OSTree.check_version(2017, 8): for path in ['/ostree/repo', '/install/ostree/repo']: if os.path.isdir(path + '/objects'): pull_opts['localcache-repos'] = Variant('as', [path]) break try: repo.pull_with_options(ostreesetup.remote, Variant('a{sv}', pull_opts), progress, cancellable) except GError as e: exn = PayloadInstallError("Failed to pull from repository: %s" % e) log.error(str(exn)) if errors.errorHandler.cb(exn) == errors.ERROR_RAISE: progressQ.send_quit(1) util.ipmi_abort(scripts=self.data.scripts) sys.exit(1) log.info("ostree pull: %s", progress.get_status() or "") progressQ.send_message(_("Preparing deployment of %s") % (ref, )) # Now that we have the data pulled, delete the remote for now. # This will allow a remote configuration defined in the tree # (if any) to override what's in the kickstart. Otherwise, # we'll re-add it in post. Ideally, ostree would support a # pull without adding a remote, but that would get quite # complex. repo.remote_delete(self.data.ostreesetup.remote, None) self._safeExecWithRedirect("ostree", ["admin", "--sysroot=" + util.getTargetPhysicalRoot(), "os-init", ostreesetup.osname]) admin_deploy_args = ["admin", "--sysroot=" + util.getTargetPhysicalRoot(), "deploy", "--os=" + ostreesetup.osname] admin_deploy_args.append(ostreesetup.remote + ':' + ref) log.info("ostree admin deploy starting") progressQ.send_message(_("Deployment starting: %s") % (ref, )) self._safeExecWithRedirect("ostree", admin_deploy_args) log.info("ostree admin deploy complete") progressQ.send_message(_("Deployment complete: %s") % (ref, )) # Reload now that we've deployed, find the path to the new deployment sysroot.load(None) deployments = sysroot.get_deployments() assert len(deployments) > 0 deployment = deployments[0] deployment_path = sysroot.get_deployment_directory(deployment) util.setSysroot(deployment_path.get_path()) try: self._copyBootloaderData() except (OSError, RuntimeError) as e: exn = PayloadInstallError("Failed to copy bootloader data: %s" % e) log.error(str(exn)) if errors.errorHandler.cb(exn) == errors.ERROR_RAISE: progressQ.send_quit(1) util.ipmi_abort(scripts=self.data.scripts) sys.exit(1) mainctx.pop_thread_default()
def startServer(self): self.log.info(_("Starting VNC...")) network.wait_for_connectivity() # Lets call it from here for now. try: self.initialize() except (socket.herror, dbus.DBusException, ValueError) as e: stdoutLog.critical("Could not initialize the VNC server: %s", e) util.ipmi_abort(scripts=self.anaconda.ksdata.scripts) sys.exit(1) if self.password and (len(self.password) < 6 or len(self.password) > 8): self.changeVNCPasswdWindow() if not self.password: SecurityTypes = "None" rfbauth = "0" else: SecurityTypes = "VncAuth" rfbauth = self.pw_file # Create the password file. self.setVNCPassword() # Lets start the xvnc. xvnccommand = [ XVNC_BINARY_NAME, ":%s" % constants.X_DISPLAY_NUMBER, "-depth", "16", "-br", "IdleTimeout=0", "-auth", "/dev/null", "-once", "DisconnectClients=false", "desktop=%s" % (self.desktop, ), "SecurityTypes=%s" % SecurityTypes, "rfbauth=%s" % rfbauth ] try: util.startX(xvnccommand, output_redirect=self.openlogfile(), timeout=self.timeout) except OSError: stdoutLog.critical("Could not start the VNC server. Aborting.") util.ipmi_abort(scripts=self.anaconda.ksdata.scripts) sys.exit(1) self.log.info(_("The VNC server is now running.")) # Lets tell the user what we are going to do. if self.vncconnecthost != "": self.log.warning( _("\n\nYou chose to connect to a listening vncviewer. \n" "This does not require a password to be set. If you \n" "set a password, it will be used in case the connection \n" "to the vncviewer is unsuccessful\n\n")) elif self.password == "": self.log.warning( _("\n\nWARNING!!! VNC server running with NO PASSWORD!\n" "You can use the vncpassword=PASSWORD boot option\n" "if you would like to secure the server.\n\n")) elif self.password != "": self.log.warning( _("\n\nYou chose to execute vnc with a password. \n\n")) else: self.log.warning(_("\n\nUnknown Error. Aborting. \n\n")) util.ipmi_abort(scripts=self.anaconda.ksdata.scripts) sys.exit(1) # Lets try to configure the vnc server to whatever the user specified if self.vncconnecthost != "": connected = self.connectToView() if not connected: self.VNCListen() else: self.VNCListen() # Start vncconfig for copy/paste self.startVncConfig()
def prompt(self, args=None): prompt = super().prompt(args) # TRANSLATORS: 's' to rescan devices prompt.add_option(C_('TUI|Spoke Navigation|Partitioning', 's'), _("rescan devices")) return prompt
def refresh(self, args=None): """Refresh the window.""" super().refresh(args) self.window.add(TextWidget(_(WARNING_SMT_ENABLED_TUI)))
def parseKickstart(handler, f, strict_mode=False, pass_to_boss=False): # preprocessing the kickstart file has already been handled in initramfs. ksparser = AnacondaKSParser(handler) kswarnings = [] showwarning = warnings.showwarning def ksshowwarning(message, category, filename, lineno, file=None, line=None): # Print the warning with default function. showwarning(message, category, filename, lineno, file, line) # Collect pykickstart warnings. if issubclass(category, KickstartParseWarning): kswarnings.append(message) try: # Process warnings differently in this part. with warnings.catch_warnings(): # Set up the warnings module. warnings.showwarning = ksshowwarning warnings.simplefilter("always", category=KickstartParseWarning) # Parse the kickstart file in DBus modules. if pass_to_boss: boss = BOSS.get_proxy() report = KickstartReport.from_structure( boss.ReadKickstartFile(f)) for warn in report.warning_messages: warnings.warn(warn.message, KickstartParseWarning) if not report.is_valid(): message = "\n\n".join(map(str, report.error_messages)) raise KickstartError(message) # Parse the kickstart file in anaconda. ksparser.readKickstart(f) # Process pykickstart warnings in the strict mode: if strict_mode and kswarnings: raise KickstartError( "Please modify your kickstart file to fix the warnings " "or remove the `ksstrict` option.") except KickstartError as e: # We do not have an interface here yet, so we cannot use our error # handling callback. parsing_log.error(e) # Print kickstart warnings in the strict mode. if strict_mode and kswarnings: print( _("\nSome warnings occurred during reading the kickstart file:" )) for w in kswarnings: print(str(w).strip()) # Print an error and terminate. print( _("\nAn error occurred during reading the kickstart file:" "\n%s\n\nThe installer will now terminate.") % str(e).strip()) util.ipmi_report(IPMI_ABORTED) time.sleep(10) sys.exit(1)
def check_memory(anaconda, options, display_mode=None): """Check is the system has enough RAM for installation. :param anaconda: instance of the Anaconda class :param options: command line/boot options :param display_mode: a display mode to use for the check (graphical mode usually needs more RAM, etc.) """ from pyanaconda import isys reason_strict = _("%(product_name)s requires %(needed_ram)s MB of memory to " "install, but you only have %(total_ram)s MB on this machine.\n") reason_graphical = _("The %(product_name)s graphical installer requires %(needed_ram)s " "MB of memory, but you only have %(total_ram)s MB.\n") reboot_extra = _('\n' 'Press [Enter] to reboot your system.\n') livecd_title = _("Not enough RAM") livecd_extra = _(" Try the text mode installer by running:\n\n" "'/usr/bin/liveinst -T'\n\n from a root terminal.") nolivecd_extra = _(" Starting text mode.") # skip the memory check in rescue mode if options.rescue: return if not display_mode: display_mode = anaconda.display_mode reason = reason_strict total_ram = int(isys.total_memory() / 1024) needed_ram = int(isys.MIN_RAM) graphical_ram = int(isys.MIN_GUI_RAM) # count the squashfs.img in if it is kept in RAM if not util.persistent_root_image(): needed_ram += isys.SQUASHFS_EXTRA_RAM graphical_ram += isys.SQUASHFS_EXTRA_RAM log.info("check_memory(): total:%s, needed:%s, graphical:%s", total_ram, needed_ram, graphical_ram) if not options.memcheck: log.warning("CHECK_MEMORY DISABLED") return reason_args = {"product_name": product.productName, "needed_ram": needed_ram, "total_ram": total_ram} if needed_ram > total_ram: if options.liveinst: # pylint: disable=logging-not-lazy stdout_log.warning(reason % reason_args) gtk_warning(livecd_title, reason % reason_args) else: reason += reboot_extra print(reason % reason_args) print(_("The installation cannot continue and the system will be rebooted")) print(_("Press ENTER to continue")) input() util.ipmi_report(constants.IPMI_ABORTED) sys.exit(1) # override display mode if machine cannot nicely run X if display_mode != constants.DisplayModes.TUI and not flags.usevnc: needed_ram = graphical_ram reason_args["needed_ram"] = graphical_ram reason = reason_graphical if needed_ram > total_ram: if options.liveinst: reason += livecd_extra # pylint: disable=logging-not-lazy stdout_log.warning(reason % reason_args) title = livecd_title gtk_warning(title, reason % reason_args) util.ipmi_report(constants.IPMI_ABORTED) sys.exit(1) else: reason += nolivecd_extra # pylint: disable=logging-not-lazy stdout_log.warning(reason % reason_args) anaconda.display_mode = constants.DisplayModes.TUI time.sleep(2)