summaryrefslogtreecommitdiffstats
path: root/ipaserver/advise/base.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipaserver/advise/base.py')
-rw-r--r--ipaserver/advise/base.py169
1 files changed, 169 insertions, 0 deletions
diff --git a/ipaserver/advise/base.py b/ipaserver/advise/base.py
new file mode 100644
index 000000000..4b6ee96f8
--- /dev/null
+++ b/ipaserver/advise/base.py
@@ -0,0 +1,169 @@
+#!/usr/bin/python
+# Authors: Tomas Babej <tbabej@redhat.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+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)