From c81849712f8888e6f12b7c2b7ebfcf5d2294addd Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Mon, 10 Jun 2013 14:43:24 +0200 Subject: Provide ipa-advise tool Provides a pluggable framework for generating configuration scriptlets and instructions for various machine setups and use cases. Creates a new ipa-advise command, available to root user on the IPA server. Also provides an example configuration plugin, config-fedora-authconfig. https://fedorahosted.org/freeipa/ticket/3670 --- ipaserver/advise/__init__.py | 22 ++++ ipaserver/advise/base.py | 169 ++++++++++++++++++++++++++ ipaserver/advise/plugins/__init__.py | 22 ++++ ipaserver/advise/plugins/fedora_authconfig.py | 41 +++++++ 4 files changed, 254 insertions(+) create mode 100644 ipaserver/advise/__init__.py create mode 100644 ipaserver/advise/base.py create mode 100644 ipaserver/advise/plugins/__init__.py create mode 100644 ipaserver/advise/plugins/fedora_authconfig.py (limited to 'ipaserver/advise') diff --git a/ipaserver/advise/__init__.py b/ipaserver/advise/__init__.py new file mode 100644 index 00000000..4fdade51 --- /dev/null +++ b/ipaserver/advise/__init__.py @@ -0,0 +1,22 @@ +# Authors: Tomas Babej +# +# Copyright (C) 2013 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +Base subpackage for ipa-advise related code. +""" diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py new file mode 100644 index 00000000..4b6ee96f --- /dev/null +++ b/ipaserver/advise/base.py @@ -0,0 +1,169 @@ +#!/usr/bin/python +# Authors: Tomas Babej +# +# Copyright (C) 2013 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +from ipalib import api +from ipalib.errors import ValidationError +from ipapython import admintool + + +""" +To add configuration instructions for a new use case, define a new class that +inherits from Advice class. + +You should create a plugin file for it in ipaserver/advise/plugins folder. + +The class can run any arbitrary code or IPA command via api.Command['command']() +calls. It needs to override get_info() method, which returns the formatted +advice string. + +>>> class sample_advice(Advice): +>>> description = 'Instructions for machine with SSSD 1.0 setup.' + +Description provided shows itself as a header and in the list of all advices +currently available via ipa-advise. + +Optionally, you can require root privileges for your plugin: + +>>> require_root = True + +The following method should be implemented in your plugin: + +>>>.....def get_info(): +>>> self.log.debug('Entering execute() method') +>>> self.log.comment('Providing useful advice just for you') +>>> self.log.command('yum update sssd -y') + +As you can see, Advice's log has 3 different levels. Debug lines are printed +out with '# DEBUG:' prefix if --verbose had been used. Comment lines utilize +'# ' prefix and command lines are printed raw. + +As a result, you can redirect the advice's output directly to a script file. + +# ipa-advise sample-advice > script.sh +# ./script.sh + +Important! Do not forget to register the class to the API. + +>>> api.register(sample_advice) +""" + + +class IpaAdvise(admintool.AdminTool): + """ + Admin tool that given systems's configuration provides instructions how to + configure the systems for various use cases. + """ + + command_name = 'ipa-advise' + usage = "%prog ADVICE" + description = "Provides configuration advice for various use cases. To "\ + "see the list of possible ADVICEs, run ipa-advise without "\ + "any arguments." + + def __init__(self, options, args): + super(IpaAdvise, self).__init__(options, args) + + @classmethod + def add_options(cls, parser): + super(IpaAdvise, cls).add_options(parser) + + def validate_options(self): + super(IpaAdvise, self).validate_options(needs_root=False) + + if len(self.args) > 1: + raise self.option_parser.error("You can only provide one " + "positional argument.") + + def log_success(self): + pass + + def print_config_list(self): + self.print_header('List of available advices') + + max_keyword_len = max((len(keyword) for keyword in api.Advice)) + + for keyword in api.Advice: + advice = getattr(api.Advice, keyword, '') + description = getattr(advice, 'description', '') + keyword = keyword.replace('_', '-') + + # Compute the number of spaces needed for the table to be aligned + offset = max_keyword_len - len(keyword) + print(" {key} {off}: {desc}".format(key=keyword, + desc=description, + off=' ' * offset)) + + def print_header(self, header, print_shell=False): + header_size = len(header) + + prefix = '' + if print_shell: + prefix = '# ' + print '#!/bin/sh' + + # Do not print out empty header + if header_size > 0: + print(prefix + '-' * (header_size - len(prefix))) + print(prefix + header) + print(prefix + '-' * (header_size - len(prefix))) + + def print_advice(self, keyword): + advice = getattr(api.Advice, keyword, None) + + # Ensure that Configuration class for given --setup option value exists + if advice is None: + raise ValidationError( + name="advice", + error="No instructions are available for '{con}'. " + "See the list of available configuration " + "by invoking the ipa-advise command with no argument." + .format(con=keyword.replace('_', '-'))) + + # Check whether root privileges are needed + if advice.require_root and os.getegid() != 0: + raise admintool.ScriptError( + 'Must be root to get advice for {adv}' + .format(adv=keyword.replace('_', '-')), 1) + + # Print out nicely formatted header + self.print_header(advice.description, print_shell=True) + + # Set options so that plugin can use verbose/quiet options + advice.set_options(self.options) + + # Print out the actual advice + advice.get_info() + for line in advice.log.content: + print line + + def run(self): + super(IpaAdvise, self).run() + + api.bootstrap(in_server=False, context='advise') + api.finalize() + + # With no argument, print the list out and exit + if not self.args: + self.print_config_list() + return + else: + keyword = self.args[0].replace('-', '_') + self.print_advice(keyword) diff --git a/ipaserver/advise/plugins/__init__.py b/ipaserver/advise/plugins/__init__.py new file mode 100644 index 00000000..2d561b41 --- /dev/null +++ b/ipaserver/advise/plugins/__init__.py @@ -0,0 +1,22 @@ +# Authors: Tomas Babej +# +# Copyright (C) 2013 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +""" +Provides a separate api for ipa-advise plugins. +""" diff --git a/ipaserver/advise/plugins/fedora_authconfig.py b/ipaserver/advise/plugins/fedora_authconfig.py new file mode 100644 index 00000000..915877db --- /dev/null +++ b/ipaserver/advise/plugins/fedora_authconfig.py @@ -0,0 +1,41 @@ +# Authors: Tomas Babej +# +# Copyright (C) 2013 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +from ipalib import api +from ipalib.frontend import Advice + + +class config_fedora_authconfig(Advice): + """ + Provides client configuration instructions using authconfig. + """ + + description = 'Authconfig instructions for configuring Fedora 18/19 '\ + 'client with IPA server without use of SSSD.' + + def get_info(self): + self.log.debug("Hostname obtained via api.env.host") + self.log.comment("Run the following command as a root:") + template = "/sbin/authconfig --enableldap --ldapserver={server} "\ + "--enablerfc2307bis --enablekrb5" + advice = template.format(server=api.env.host) + self.log.command(advice) + + +api.register(config_fedora_authconfig) -- cgit