diff options
Diffstat (limited to 'base/all/root/scripts/cluster_configure/cluster-configure.py')
-rwxr-xr-x | base/all/root/scripts/cluster_configure/cluster-configure.py | 452 |
1 files changed, 0 insertions, 452 deletions
diff --git a/base/all/root/scripts/cluster_configure/cluster-configure.py b/base/all/root/scripts/cluster_configure/cluster-configure.py deleted file mode 100755 index e94579e..0000000 --- a/base/all/root/scripts/cluster_configure/cluster-configure.py +++ /dev/null @@ -1,452 +0,0 @@ -#!/usr/bin/env python - -# Configure clustered Samba nodes - -# Copyright (C) Martin Schwenke 2010 - -# 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 -import sys -import optparse # Newer argparse not yet in RHEL5/EPEL. -import ConfigParser -import logging -import re -import subprocess -import errno -import filecmp -import shutil -import glob -import string - -lib_dir = "lib" -sys.path.append(lib_dir) - -import logging - -def process_args(): - usage = "usage: %prog [options] configfile" - - parser = optparse.OptionParser(usage=usage) - - parser.add_option("-t", "--templates", - action="store", dest="templates", - default="templates/rhel", - help="directory containing templates") - parser.add_option("-f", "--force", - action="store_true", dest="force", - default=False, - help="install configuration files even if unchanged",) - parser.add_option("-r", "--no-reload", - action="store_true", dest="no_reload", - default=False, - help="""don't run the "reload" script for -services - this is usually done to make services reload their configuration -after any changes""") - parser.add_option("-n", "--simulate", - action="store_true", dest="simulate", - default=False, - help="""don't actually install configuration -files - this will leave the configuration files in the temporary staging -area - implies -r""") - parser.add_option("-s", "--staging", - action="store", dest="staging", - default="staging", - help="directory to stage the files to be installed") - parser.add_option("-v", "--verbose", - action="count", dest="verbose", - default=0, - help="print information and actions taken to stdout") - parser.add_option("-l", "--log-file", - action="store", dest="log_file", default=None, - metavar="FILE", - help="append information and actions taken to FILE") - (options, args) = parser.parse_args() - - if len(args) != 1: - parser.error("configuration file must be specified") - - options.config = args[0] - - return options - -def setup_logging(): - - global options - - logger = logging.getLogger("cluster-configure") - logger.setLevel(logging.ERROR) - - sh = logging.StreamHandler(sys.stderr) - sh.handleError = lambda x : (logging.shutdown(), sys.exit()) - logger.addHandler(sh) - - if options.verbose == 1: - logger.setLevel(logging.WARNING) - elif options.verbose == 2: - logger.setLevel(logging.INFO) - elif options.verbose >= 3: - logger.setLevel(logging.DEBUG) - - if options.log_file is not None: - fh = logging.FileHandler(options.log_file) - # The formatting option %(funcName)s would be useful here but - # it only appeared in Python 2.5 and this script will be run - # on Python 2.4. - formatter = logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s") - fh.setFormatter(formatter) - logger.addHandler(fh) - - return logger - -def _rpm_parse_version(v): - ret = [] - for x in re.split("[^A-Za-z0-9]", v): - try: - ret.append(int(x)) - except ValueError: - ret.append(x) - return ret - -config = None -options = None -logger = None - -class Package(object): - def __init__(self, config, directory): - self.config = config - self.directory = directory - self.version = None - self.platform = None - self.version_dir = None - self.files = list() - self.files_to_install = list() - - m = re.match("\d\d\.(.*)", directory) - if m is None: - raise RuntimeError, \ - ("invalid template package directory %s" % directory) - self.name = m.group(1) - self.stage = os.path.join(options.staging, self.name) - - def _get_version(self): - if os.path.exists("/etc/redhat-release"): - p = subprocess.Popen(["rpm", "-q", self.name], - stdout=subprocess.PIPE) - out = p.communicate()[0] - status = p.wait() - if status == 0: - out.replace(self.name + "-", "") - self.version = string.strip(out) - self.platform = "rpm" - logger.debug("_get_version: package %s has version %s", - self.name, self.version) - return True - elif os.path.exists("/etc/debian_version"): - p = subprocess.Popen(["dpkg-query", "-W", "-f", "${Version}", - self.name], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - out = p.communicate()[0] - status = p.wait() - if status == 0 and len(out) > 0: - self.version = string.strip(out) - self.platform = "deb" - logger.debug("_get_version: package %s has version %s", - self.name, self.version) - return True - - logger.info("_get_version: package %s is not installed", self.name) - return False - - # Currently Red Hat specific... - def _check_version(self, min=None, max=None): - v = _rpm_parse_version(self.version) - return min is None or \ - (_rpm_parse_version(min) <= v and \ - (max is None or v < _rpm_parse_version(max))) - - def find_version_dir(self): - if self._get_version(): - pdir = os.path.join(options.templates, self.directory) - versions = sorted(os.listdir(pdir)) - # FIXME: filter out any without #, since they're meaningless. - versions.reverse() # bare "#" comes last since it is the default - - for i in versions: - try: - (min, max) = map(lambda x: len(x) and x or None, i.split("#")) - except ValueError: - logger.warn("_find_version_dir: skipping invalid version subdirectory %s" % i) - continue - - if self._check_version(min, max): - self.version_dir = os.path.join(pdir, i) - logger.info("_find_version_dir: found version directory %s" % self.version_dir) - return True - return False - - def _find_template_files(self, fdir): - """Find the available templates in the given file directory fdir.""" - for (root, dirs, files) in os.walk(fdir): - for f in files: - # os.path.relpath is not available in older Python - frel = os.path.join(root, f).replace(fdir + "/", "", 1) - logger.debug("_find_template_files: add template file %s" % frel) - self.files.append(frel) - - def _substitute_template(self, contents): - """Expand the given template fdir/file into the staging area.""" - - logger.debug("_expand_template: subsitute variables into %s", file) - - # Find variables in template and substitute values. - variables = sorted(set(re.findall("!!((\w+)((=)([^!]*))?)!!", - contents))) - # r is the default replacement value for v. - for (a, v, x, e, r) in variables: - try: - r = self.config.get("package:" + self.name, v) - except (ConfigParser.NoSectionError, ConfigParser.NoOptionError), s: - # This is the equals sign. if it is there then we have default. - if e == "": - raise - - contents = contents.replace("!!%s!!" % a, r) - - # Find plugin expressions in template and subsitute values. - exprs = re.findall("!!%([^!]+)!!", contents) - for e in exprs: - (m, f, rest) = re.split("[:(]", e, 2) - foo = 'plugins["%s"].%s(config, "%s", %s' % (m, f, self.name, rest) - r = eval(foo) - if r is None: - return None - contents = contents.replace("!!%%%s!!" % e, r) - - # Find general python expressions in template and subsitute values. - exprs = re.findall("!!\|([^!]+)!!", contents) - for e in exprs: - r = eval(e) - if r is None: - return None - contents = contents.replace("!!|%s!!" % e, r) - - return contents - - def _expand_template(self, fdir, file): - """Expand the given template fdir/file into the staging area.""" - - logger.debug("_expand_template: subsitute variables into %s", file) - - # Read input file. - src = os.path.join(fdir, file) - f = open(src) - contents = f.read() - f.close() - - contents = self._substitute_template(contents) - - # Ensure output directory exists in staging area. - dst = os.path.join(self.stage, file) - try: - os.makedirs(os.path.dirname(dst)) - except OSError, exc: - if exc.errno == errno.EEXIST: - pass - else: raise - - # Write output file into staging area, unless it is None, - # which means to remove the file if it exists. - if contents is not None: - f = open(dst, "w") - f.write(contents) - f.close() - else: - try: - os.remove(dst) - except OSError, exc: - if exc.errno == errno.ENOENT: - pass - else: raise - - def _would_install_file(self, file): - """Check if a file should be installed from the staging area - because it is different to the currently installed file (or if - there is no installed file).""" - - src = os.path.join(self.stage, file) - dst = os.path.join("/", file) - - if not os.path.exists(src) and not os.path.exists(dst): - logger.debug("_would_install_file: skip install of %s (missing)", dst) - return False - - try: - if not options.force and filecmp.cmp(src, dst, shallow=False): - logger.debug("_would_install_file: skip install of %s (unchanged)", dst) - return False - except OSError, exc: - if exc.errno == errno.ENOENT: - pass - else: raise - - logger.info("_would_install_file: would install file %s", dst) - return True - - def would_install_files(self): - """For the templates in our packages files area, expand each - template into the staging area and check if it would be - installed due to a change. Return True if any files would be - installed, False otherwise.""" - - fdir = os.path.join(self.version_dir, "files") - - shutil.rmtree(self.stage, ignore_errors=True) - - self._find_template_files(fdir) - for f in self.files: - self._expand_template(fdir, f) - - for f in self.files: - if self._would_install_file(f): - self.files_to_install.append(f) - - return self.files_to_install - - def _install_file(self, file): - """Install file from the staging area .""" - - src = os.path.join(self.stage, file) - dst = os.path.join("/", file) - - if os.path.exists(src): - logger.info("_install_file: install file %s", dst) - shutil.copy2(src, dst) - else: - logger.info("_install_file: remove file %s", dst) - try: - os.remove(dst) - except OSError, exc: - if exc.errno == errno.ENOENT: - pass - else: raise - - def install_files(self): - """Install the list of files from self.files_to_install.""" - - for f in self.files_to_install: - self._install_file(f) - - def run_event(self, event): - """Run the given event script for the service, if present.""" - es = os.path.join(self.version_dir, "events", event) - if os.path.exists(es) and os.access(es, os.X_OK): - p = subprocess.Popen([es], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - out = p.communicate()[0] - status = p.wait() - if status == 0: - logger.info("_run_event: successfully ran event script %s", es) - else: - logger.error("""_run_event: error running event script "%s": -"%s" """, - es , string.strip(out)) - else: - logger.debug("_run_event: no event script %s in %s", event, - self.version_dir) - - -plugins = {} - -def load_plugins(): - global plugins - - plugin_dir = "plugins" - - sys.path.append(plugin_dir) - for f in map(lambda x: os.path.splitext(os.path.basename(x))[0], - glob.glob(os.path.join(plugin_dir, "*.py"))): - - plugins[f] = __import__(f) - -def ctdb_socket(): - - ret = os.getenv('CTDB_SOCKET') - - if ret is None: - ctdb = '/usr/bin/ctdb' - if os.path.exists(ctdb): - cmd = "strings " + ctdb + " | grep -E '/ctdbd?\.socket$'" - p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) - out = p.communicate()[0] - status = p.wait() - if status == 0: - ret = string.rstrip(out) - else: - logger.warning('Failed to find socket path in "' + ctdb + - '" - falling back to default') - else: - logger.warning('Failed to find "' + ctdb + - '" - falling back to default') - if ret is None: - ret = '/var/run/ctdb/ctdbd.socket' - - return ret - -def main(): - global config, options, logger - - options = process_args() - - logger = setup_logging() - - load_plugins() - - os.environ["PATH"] = os.getcwd() + ":" + os.environ["PATH"] - - logger.debug("main: read configuration from %s", options.config) - config = ConfigParser.SafeConfigParser() - config.readfp(open(options.config)) - - # Run the check function in every plugin that defines it. - config_status = True - for p in plugins.iterkeys(): - func = getattr(plugins[p], "check", None) - if callable(func): - if not func(config): - config_status = False - if not config_status: - logger.error("main: exiting due to previous configuration errors") - return 1 - - # Process templates. - for d in sorted(os.listdir(options.templates)): - try: - p = Package(config, d) - if p.find_version_dir(): - if p.would_install_files() and not options.simulate: - p.run_event("pre") - p.install_files() - p.run_event("post") - except RuntimeError, s: - logger.info("main: ignoring %s", s) - - logging.shutdown() - - -if __name__ == '__main__': - sys.exit(main()) |