As it has been decided to implement support for the realm command directly in anaconda, this patch adds support for the realm command. It needs the corresponding realm patch for pykickstart, which handles command parsing.
The patch is based on Stef's original realm patch and Vratislav's realm addon.
Martin Kolman (1): Add support for the realm command
anaconda.spec.in | 1 + pyanaconda/install.py | 26 +++++++++++--- pyanaconda/kickstart.py | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-)
This patch adds support for the realm command directly to Anaconda, so that realm can be specified directly with the realm command without the addon data notation.
The realm command enables joining a domain during installation. It basically just passes the arguments to realmd and parses the output for success/failure. The join, permit and deny commands are supported.
This patch is based on the original Stef's patch and the Vratislav's realm addon.
Signed-off-by: Martin Kolman mkolman@gmail.com --- anaconda.spec.in | 1 + pyanaconda/install.py | 26 +++++++++++--- pyanaconda/kickstart.py | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-)
diff --git a/anaconda.spec.in b/anaconda.spec.in index df53d08..50f4777 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -98,6 +98,7 @@ Requires: tigervnc-server-minimal Requires: pytz Requires: libxklavier Requires: libgnomekbd +Requires: realmd %ifarch %livearches Requires: usermode Requires: zenity diff --git a/pyanaconda/install.py b/pyanaconda/install.py index 0041778..3e9084c 100644 --- a/pyanaconda/install.py +++ b/pyanaconda/install.py @@ -51,7 +51,13 @@ def _writeKS(ksdata): def doConfiguration(storage, payload, ksdata, instClass): from pyanaconda.kickstart import runPostScripts
- progressQ.send_init(5) + step_count = 5 + # if a realm was discovered, + # increment the counter as the + # real joining step will be executed + if ksdata.realm.discovered: + step_count = 6 + progressQ.send_init(step_count)
# Now run the execute methods of ksdata that require an installed system # to be present first. @@ -81,7 +87,11 @@ def doConfiguration(storage, payload, ksdata, instClass): with progress_report(_("Configuring addons")): ksdata.addons.execute(storage, ksdata, instClass, u) ksdata.configured_spokes.execute(storage, ksdata, instClass, u) - + + if ksdata.realm.discovered: + with progress_report(_("Joining realm: %s" % ksdata.realm.discovered)): + ksdata.realm.execute(storage, ksdata, instClass) + with progress_report(_("Running post-installation scripts")): runPostScripts(ksdata.scripts)
@@ -107,7 +117,9 @@ def doInstall(storage, payload, ksdata, instClass): # those are the ones that take the most time. steps = len(storage.devicetree.findActions(type="create", object="format")) + \ len(storage.devicetree.findActions(type="resize", object="format")) - steps += 5 # pre setup phase, packages setup, packages, bootloader, post install + steps += 6 + # pre setup phase, packages setup, packages, bootloader, realmd, + # post install progressQ.send_init(steps)
with progress_report(_("Setting up the installation environment")): @@ -122,10 +134,16 @@ def doInstall(storage, payload, ksdata, instClass):
# Do packaging.
+ # Discover information about realms to join, + # to determine additional packages + if ksdata.realm.join_realm: + with progress_report(_("Discovering realm to join")): + ksdata.realm.setup() + # anaconda requires storage packages in order to make sure the target # system is bootable and configurable, and some other packages in order # to finish setting up the system. - packages = storage.packages + ["authconfig", "firewalld"] + packages = storage.packages + ["authconfig", "firewalld"] + ksdata.realm.packages payload.preInstall(packages=packages, groups=payload.languageGroups()) payload.install()
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index fd71f70..eab21ff 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -37,6 +37,7 @@ import iutil import os import os.path import tempfile +import subprocess import flags as flags_module from flags import flags from constants import * @@ -370,6 +371,99 @@ class BTRFSData(commands.btrfs.F17_BTRFSData):
storage.createDevice(request)
+ +class Realm(commands.realm.F19_Realm): + def __init__(self, *args): + commands.realm.F19_Realm.__init__(self, *args) + self.packages = [] + self.discovered = "" + + def setup(self): + if not self.join_realm: + return + + try: + argv = ["realm", "discover", "--verbose"] + \ + self.discover_options + [self.join_realm] + proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + output, stderr = proc.communicate() + # might contain useful information for users who use + # use the realm kickstart command + log.info("Realm discover stderr:\n%s" % stderr) + except OSError as msg: + # TODO: A lousy way of propagating what will usually be + # 'no such realm' + log.error("Error running realm %s: %s", argv, msg) + return + + # Now parse the output for the required software. First line is the + # realm name, and following lines are information as "name: value" + self.packages = ["realmd"] + self.discovered = "" + + lines = output.split("\n") + if not lines: + return + self.discovered = lines.pop(0).strip() + log.info("Realm discovered: %s" % self.discovered) + for line in lines: + parts = line.split(":", 1) + if len(parts) == 2 and parts[0].strip() == "required-package": + self.packages.append(parts[1].strip()) + + log.info("Realm %s needs packages %s" % + (self.discovered, ", ".join(self.packages))) + + def execute(self, *args): + if not self.discovered: + return + for arg in self.join_args: + if arg.startswith("--no-password") or arg.startswith("--one-time-password"): + pw_args = [] + break + else: + # no explicit password arg using implicit --no-password + pw_args = ["--no-password"] + + argv = ["realm", "join", "--install", ROOT_PATH, "--verbose"] + \ + pw_args + self.join_args + rc = -1 + try: + proc = subprocess.Popen(argv, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + output, stderr = proc.communicate() + # might contain useful information for users who use + # use the realm kickstart command + log.info("Realm join stderr:\n%s" % stderr) + rc = proc.returncode + except OSError as msg: + log.error("Error running %s: %s", argv, msg) + + if rc != 0: + log.error("Command failure: %s: %d", argv, rc) + return + + log.info("Joined realm %s", self.join_realm) + + for (command, options) in self.after: + rc = -1 + argv = ["realm", command, "--install", ROOT_PATH, "--verbose"] + options + try: + proc = subprocess.Popen(argv, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + output, stderr = proc.communicate() + # might contain useful information for users who use + # use the realm kickstart command + log.info("Realm additional commands stderr:\n%s" % stderr) + rc = proc.returncode + except OSError as msg: + log.error("Error running %s: %s", argv, msg) + if rc != 0: + log.error("Command failure: %s: %d", argv, rc) + + log.info("Ran %s", argv) + + class ClearPart(commands.clearpart.F17_ClearPart): def parse(self, args): retval = commands.clearpart.F17_ClearPart.parse(self, args) @@ -1374,6 +1468,7 @@ commandMap = { "part": Partition, "partition": Partition, "raid": Raid, + "realm": Realm, "rootpw": RootPw, "selinux": SELinux, "services": Services,
@@ -81,7 +87,11 @@ def doConfiguration(storage, payload, ksdata, instClass): with progress_report(_("Configuring addons")): ksdata.addons.execute(storage, ksdata, instClass, u) ksdata.configured_spokes.execute(storage, ksdata, instClass, u)
- if ksdata.realm.discovered:
with progress_report(_("Joining realm: %s" % ksdata.realm.discovered)):
The above line should look like this instead:
with progress_report(_("Joining realm: %s") % ksdata.realm.discovered):
The original line will end up trying to translate the string "Joining realm: wherever", which is not going to succeed because we won't have that source string. In general, you should never do string substitutions inside a _() call.
- def execute(self, *args):
if not self.discovered:returnfor arg in self.join_args:if arg.startswith("--no-password") or arg.startswith("--one-time-password"):pw_args = []break
As long as these args don't take options (--no-password=, for instance), you can instead do:
args = self.join_args if "--no-password" in args or "--one-time-password" in args: .... else: ....
- Chris
On 30.04.2013 16:47, Chris Lumens wrote:
- def execute(self, *args):
if not self.discovered:returnfor arg in self.join_args:if arg.startswith("--no-password") or arg.startswith("--one-time-password"):pw_args = []breakAs long as these args don't take options (--no-password=, for instance), you can instead do:
args = self.join_args if "--no-password" in args or "--one-time-password" in args:
For the record, --one-time-password takes an argument and --no-password doesn't.
Cheers,
Stef
On Mon, 2013-04-29 at 15:13 +0200, Martin Kolman wrote:
As it has been decided to implement support for the realm command directly in anaconda, this patch adds support for the realm command. It needs the corresponding realm patch for pykickstart, which handles command parsing.
I'd just like to add that the intention here is to have the kickstart support for the realm commands as integral part of the Anaconda and anything else like GUI or TUI living in an addon. This seems to me as a good trade off between having realm support look like an integral part of the Anaconda and not adding a lot of code and magic to the Anaconda and pykickstart.
I'd just like to add that the intention here is to have the kickstart support for the realm commands as integral part of the Anaconda and anything else like GUI or TUI living in an addon. This seems to me as a good trade off between having realm support look like an integral part of the Anaconda and not adding a lot of code and magic to the Anaconda and pykickstart.
Yeah, I think this is a good plan. I'm looking at the patches themselves right now.
- Chris
anaconda-patches@lists.fedorahosted.org