summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Admin
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Bcfg2/Server/Admin')
-rw-r--r--src/lib/Bcfg2/Server/Admin/Backup.py22
-rw-r--r--src/lib/Bcfg2/Server/Admin/Client.py32
-rw-r--r--src/lib/Bcfg2/Server/Admin/Compare.py147
-rw-r--r--src/lib/Bcfg2/Server/Admin/Init.py353
-rw-r--r--src/lib/Bcfg2/Server/Admin/Minestruct.py56
-rw-r--r--src/lib/Bcfg2/Server/Admin/Perf.py38
-rw-r--r--src/lib/Bcfg2/Server/Admin/Pull.py147
-rw-r--r--src/lib/Bcfg2/Server/Admin/Reports.py262
-rw-r--r--src/lib/Bcfg2/Server/Admin/Syncdb.py31
-rw-r--r--src/lib/Bcfg2/Server/Admin/Viz.py104
-rw-r--r--src/lib/Bcfg2/Server/Admin/Xcmd.py54
-rw-r--r--src/lib/Bcfg2/Server/Admin/__init__.py142
12 files changed, 0 insertions, 1388 deletions
diff --git a/src/lib/Bcfg2/Server/Admin/Backup.py b/src/lib/Bcfg2/Server/Admin/Backup.py
deleted file mode 100644
index 0a04df98b..000000000
--- a/src/lib/Bcfg2/Server/Admin/Backup.py
+++ /dev/null
@@ -1,22 +0,0 @@
-""" Make a backup of the Bcfg2 repository """
-
-import os
-import time
-import tarfile
-import Bcfg2.Server.Admin
-import Bcfg2.Options
-
-
-class Backup(Bcfg2.Server.Admin.MetadataCore):
- """ Make a backup of the Bcfg2 repository """
-
- def __call__(self, args):
- datastore = self.setup['repo']
- timestamp = time.strftime('%Y%m%d%H%M%S')
- fmt = 'gz'
- mode = 'w:' + fmt
- filename = timestamp + '.tar' + '.' + fmt
- out = tarfile.open(os.path.join(datastore, filename), mode=mode)
- out.add(datastore, os.path.basename(datastore))
- out.close()
- print("Archive %s was stored under %s" % (filename, datastore))
diff --git a/src/lib/Bcfg2/Server/Admin/Client.py b/src/lib/Bcfg2/Server/Admin/Client.py
deleted file mode 100644
index 187ccfd71..000000000
--- a/src/lib/Bcfg2/Server/Admin/Client.py
+++ /dev/null
@@ -1,32 +0,0 @@
-""" Create, delete, or list client entries """
-
-import sys
-import Bcfg2.Server.Admin
-from Bcfg2.Server.Plugin import MetadataConsistencyError
-
-
-class Client(Bcfg2.Server.Admin.MetadataCore):
- """ Create, delete, or list client entries """
- __usage__ = "[options] [add|del|list] [attr=val]"
- __plugin_whitelist__ = ["Metadata"]
-
- def __call__(self, args):
- if len(args) == 0:
- self.errExit("No argument specified.\n"
- "Usage: %s" % self.__usage__)
- if args[0] == 'add':
- try:
- self.metadata.add_client(args[1])
- except MetadataConsistencyError:
- self.errExit("Error in adding client: %s" % sys.exc_info()[1])
- elif args[0] in ['delete', 'remove', 'del', 'rm']:
- try:
- self.metadata.remove_client(args[1])
- except MetadataConsistencyError:
- self.errExit("Error in deleting client: %s" %
- sys.exc_info()[1])
- elif args[0] in ['list', 'ls']:
- for client in self.metadata.list_clients():
- print(client)
- else:
- self.errExit("No command specified")
diff --git a/src/lib/Bcfg2/Server/Admin/Compare.py b/src/lib/Bcfg2/Server/Admin/Compare.py
deleted file mode 100644
index 6bb15cafd..000000000
--- a/src/lib/Bcfg2/Server/Admin/Compare.py
+++ /dev/null
@@ -1,147 +0,0 @@
-import lxml.etree
-import os
-import Bcfg2.Server.Admin
-
-
-class Compare(Bcfg2.Server.Admin.Mode):
- """ Determine differences between files or directories of client
- specification instances """
- __usage__ = ("<old> <new>\n\n"
- " -r\trecursive")
-
- def __init__(self):
- Bcfg2.Server.Admin.Mode.__init__(self)
- self.important = {'Path': ['name', 'type', 'owner', 'group', 'mode',
- 'important', 'paranoid', 'sensitive',
- 'dev_type', 'major', 'minor', 'prune',
- 'encoding', 'empty', 'to', 'recursive',
- 'vcstype', 'sourceurl', 'revision',
- 'secontext'],
- 'Package': ['name', 'type', 'version', 'simplefile',
- 'verify'],
- 'Service': ['name', 'type', 'status', 'mode',
- 'target', 'sequence', 'parameters'],
- 'Action': ['name', 'timing', 'when', 'status',
- 'command']
- }
-
- def compareStructures(self, new, old):
- if new.get("name"):
- bundle = new.get('name')
- else:
- bundle = 'Independent'
-
- identical = True
-
- for child in new.getchildren():
- if child.tag not in self.important:
- print(" %s in (new) bundle %s:\n tag type not handled!" %
- (child.tag, bundle))
- continue
- equiv = old.xpath('%s[@name="%s"]' %
- (child.tag, child.get('name')))
- if len(equiv) == 0:
- print(" %s %s in bundle %s:\n only in new configuration" %
- (child.tag, child.get('name'), bundle))
- identical = False
- continue
- diff = []
- if child.tag == 'Path' and child.get('type') == 'file' and \
- child.text != equiv[0].text:
- diff.append('contents')
- attrdiff = [field for field in self.important[child.tag] if \
- child.get(field) != equiv[0].get(field)]
- if attrdiff:
- diff.append('attributes (%s)' % ', '.join(attrdiff))
- if diff:
- print(" %s %s in bundle %s:\n %s differ" % (child.tag, \
- child.get('name'), bundle, ' and '.join(diff)))
- identical = False
-
- for child in old.getchildren():
- if child.tag not in self.important:
- print(" %s in (old) bundle %s:\n tag type not handled!" %
- (child.tag, bundle))
- elif len(new.xpath('%s[@name="%s"]' %
- (child.tag, child.get('name')))) == 0:
- print(" %s %s in bundle %s:\n only in old configuration" %
- (child.tag, child.get('name'), bundle))
- identical = False
-
- return identical
-
- def compareSpecifications(self, path1, path2):
- try:
- new = lxml.etree.parse(path1).getroot()
- except IOError:
- print("Failed to read %s" % (path1))
- raise SystemExit(1)
-
- try:
- old = lxml.etree.parse(path2).getroot()
- except IOError:
- print("Failed to read %s" % (path2))
- raise SystemExit(1)
-
- for src in [new, old]:
- for bundle in src.findall('./Bundle'):
- if bundle.get('name')[-4:] == '.xml':
- bundle.set('name', bundle.get('name')[:-4])
-
- identical = True
-
- for bundle in old.findall('./Bundle'):
- if len(new.xpath('Bundle[@name="%s"]' % (bundle.get('name')))) == 0:
- print(" Bundle %s only in old configuration" %
- bundle.get('name'))
- identical = False
- for bundle in new.findall('./Bundle'):
- equiv = old.xpath('Bundle[@name="%s"]' % (bundle.get('name')))
- if len(equiv) == 0:
- print(" Bundle %s only in new configuration" %
- bundle.get('name'))
- identical = False
- elif not self.compareStructures(bundle, equiv[0]):
- identical = False
-
- i1 = lxml.etree.Element('Independent')
- i2 = lxml.etree.Element('Independent')
- i1.extend(new.findall('./Independent/*'))
- i2.extend(old.findall('./Independent/*'))
- if not self.compareStructures(i1, i2):
- identical = False
-
- return identical
-
- def __call__(self, args):
- Bcfg2.Server.Admin.Mode.__call__(self, args)
- if len(args) == 0:
- self.errExit("No argument specified.\n"
- "Please see bcfg2-admin compare help for usage.")
- if '-r' in args:
- args = list(args)
- args.remove('-r')
- (oldd, newd) = args
- (old, new) = [os.listdir(spot) for spot in args]
- old_extra = []
- for item in old:
- if item not in new:
- old_extra.append(item)
- continue
- print("File: %s" % item)
- state = self.__call__([oldd + '/' + item, newd + '/' + item])
- new.remove(item)
- if state:
- print("File %s is good" % item)
- else:
- print("File %s is bad" % item)
- if new:
- print("%s has extra files: %s" % (newd, ', '.join(new)))
- if old_extra:
- print("%s has extra files: %s" % (oldd, ', '.join(old_extra)))
- return
- try:
- (old, new) = args
- return self.compareSpecifications(new, old)
- except IndexError:
- self.errExit(self.__call__.__doc__)
diff --git a/src/lib/Bcfg2/Server/Admin/Init.py b/src/lib/Bcfg2/Server/Admin/Init.py
deleted file mode 100644
index ba553c7ef..000000000
--- a/src/lib/Bcfg2/Server/Admin/Init.py
+++ /dev/null
@@ -1,353 +0,0 @@
-""" Interactively initialize a new repository. """
-
-import os
-import sys
-import stat
-import select
-import random
-import socket
-import string
-import getpass
-from Bcfg2.Utils import Executor
-import Bcfg2.Server.Admin
-import Bcfg2.Server.Plugin
-import Bcfg2.Options
-import Bcfg2.Server.Plugins.Metadata
-from Bcfg2.Compat import input # pylint: disable=W0622
-
-# default config file
-CONFIG = '''[server]
-repository = %s
-plugins = %s
-# Uncomment the following to listen on all interfaces
-#listen_all = true
-
-[statistics]
-sendmailpath = %s
-#web_debug = False
-#time_zone =
-
-[database]
-#engine = sqlite3
-# 'postgresql', 'mysql', 'mysql_old', 'sqlite3' or 'ado_mssql'.
-#name =
-# Or path to database file if using sqlite3.
-#<repository>/etc/bcfg2.sqlite is default path if left empty
-#user =
-# Not used with sqlite3.
-#password =
-# Not used with sqlite3.
-#host =
-# Not used with sqlite3.
-#port =
-
-[reporting]
-transport = LocalFilesystem
-
-[communication]
-protocol = %s
-password = %s
-certificate = %s
-key = %s
-ca = %s
-
-[components]
-bcfg2 = %s
-'''
-
-# Default groups
-GROUPS = '''<Groups version='3.0'>
- <Group profile='true' public='true' default='true' name='basic'>
- <Group name='%s'/>
- </Group>
- <Group name='ubuntu'/>
- <Group name='debian'/>
- <Group name='freebsd'/>
- <Group name='gentoo'/>
- <Group name='redhat'/>
- <Group name='suse'/>
- <Group name='mandrake'/>
- <Group name='solaris'/>
- <Group name='arch'/>
-</Groups>
-'''
-
-# Default contents of clients.xml
-CLIENTS = '''<Clients version="3.0">
- <Client profile="basic" name="%s"/>
-</Clients>
-'''
-
-# Mapping of operating system names to groups
-OS_LIST = [('Red Hat/Fedora/RHEL/RHAS/CentOS', 'redhat'),
- ('SUSE/SLES', 'suse'),
- ('Mandrake', 'mandrake'),
- ('Debian', 'debian'),
- ('Ubuntu', 'ubuntu'),
- ('Gentoo', 'gentoo'),
- ('FreeBSD', 'freebsd'),
- ('Arch', 'arch')]
-
-
-def safe_input(prompt):
- """ input() that flushes the input buffer before accepting input """
- # flush input buffer
- while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0:
- os.read(sys.stdin.fileno(), 4096)
- return input(prompt)
-
-
-def gen_password(length):
- """Generates a random alphanumeric password with length characters."""
- chars = string.letters + string.digits
- return "".join(random.choice(chars) for i in range(length))
-
-
-def create_key(hostname, keypath, certpath, country, state, location):
- """Creates a bcfg2.key at the directory specifed by keypath."""
- cmd = Executor(timeout=120)
- subject = "/C=%s/ST=%s/L=%s/CN=%s'" % (country, state, location, hostname)
- key = cmd.run(["openssl", "req", "-batch", "-x509", "-nodes",
- "-subj", subject, "-days", "1000", "-newkey", "rsa:2048",
- "-keyout", keypath, "-noout"])
- if not key.success:
- print("Error generating key: %s" % key.error)
- return
- os.chmod(keypath, stat.S_IRUSR | stat.S_IWUSR) # 0600
- csr = cmd.run(["openssl", "req", "-batch", "-new", "-subj", subject,
- "-key", keypath])
- if not csr.success:
- print("Error generating certificate signing request: %s" % csr.error)
- return
- cert = cmd.run(["openssl", "x509", "-req", "-days", "1000",
- "-signkey", keypath, "-out", certpath],
- inputdata=csr.stdout)
- if not cert.success:
- print("Error signing certificate: %s" % cert.error)
- return
-
-
-def create_conf(confpath, confdata):
- """ create the config file """
- # Don't overwrite existing bcfg2.conf file
- if os.path.exists(confpath):
- result = safe_input("\nWarning: %s already exists. "
- "Overwrite? [y/N]: " % confpath)
- if result not in ['Y', 'y']:
- print("Leaving %s unchanged" % confpath)
- return
- try:
- open(confpath, "w").write(confdata)
- os.chmod(confpath, stat.S_IRUSR | stat.S_IWUSR) # 0600
- except Exception:
- err = sys.exc_info()[1]
- print("Error trying to write configuration file '%s': %s" %
- (confpath, err))
- raise SystemExit(1)
-
-
-class Init(Bcfg2.Server.Admin.Mode):
- """Interactively initialize a new repository."""
-
- def __init__(self):
- Bcfg2.Server.Admin.Mode.__init__(self)
- self.data = dict()
- self.plugins = Bcfg2.Options.SERVER_PLUGINS.default
-
- def _set_defaults(self, opts):
- """Set default parameters."""
- self.data['configfile'] = opts['configfile']
- self.data['repopath'] = opts['repo']
- self.data['password'] = gen_password(8)
- self.data['server_uri'] = "https://%s:6789" % socket.getfqdn()
- self.data['sendmail'] = opts['sendmail']
- self.data['proto'] = opts['proto']
- if os.path.exists("/etc/pki/tls"):
- self.data['keypath'] = "/etc/pki/tls/private/bcfg2.key"
- self.data['certpath'] = "/etc/pki/tls/certs/bcfg2.crt"
- elif os.path.exists("/etc/ssl"):
- self.data['keypath'] = "/etc/ssl/bcfg2.key"
- self.data['certpath'] = "/etc/ssl/bcfg2.crt"
- else:
- basepath = os.path.dirname(self.configfile)
- self.data['keypath'] = os.path.join(basepath, "bcfg2.key")
- self.data['certpath'] = os.path.join(basepath, 'bcfg2.crt')
-
- def __call__(self, args):
- # Parse options
- setup = Bcfg2.Options.get_option_parser()
- setup.add_options(dict(configfile=Bcfg2.Options.CFILE,
- plugins=Bcfg2.Options.SERVER_PLUGINS,
- proto=Bcfg2.Options.SERVER_PROTOCOL,
- repo=Bcfg2.Options.SERVER_REPOSITORY,
- sendmail=Bcfg2.Options.SENDMAIL_PATH))
- opts = sys.argv[1:]
- opts.remove(self.__class__.__name__.lower())
- setup.reparse(argv=opts)
- self._set_defaults(setup)
-
- # Prompt the user for input
- self._prompt_config()
- self._prompt_repopath()
- self._prompt_password()
- self._prompt_hostname()
- self._prompt_server()
- self._prompt_groups()
- self._prompt_keypath()
- self._prompt_certificate()
-
- # Initialize the repository
- self.init_repo()
-
- def _prompt_hostname(self):
- """Ask for the server hostname."""
- data = safe_input("What is the server's hostname [%s]: " %
- socket.getfqdn())
- if data != '':
- self.data['shostname'] = data
- else:
- self.data['shostname'] = socket.getfqdn()
-
- def _prompt_config(self):
- """Ask for the configuration file path."""
- newconfig = safe_input("Store Bcfg2 configuration in [%s]: " %
- self.configfile)
- if newconfig != '':
- self.data['configfile'] = os.path.abspath(newconfig)
-
- def _prompt_repopath(self):
- """Ask for the repository path."""
- while True:
- newrepo = safe_input("Location of Bcfg2 repository [%s]: " %
- self.data['repopath'])
- if newrepo != '':
- self.data['repopath'] = os.path.abspath(newrepo)
- if os.path.isdir(self.data['repopath']):
- response = safe_input("Directory %s exists. Overwrite? [y/N]:"
- % self.data['repopath'])
- if response.lower().strip() == 'y':
- break
- else:
- break
-
- def _prompt_password(self):
- """Ask for a password or generate one if none is provided."""
- newpassword = getpass.getpass(
- "Input password used for communication verification "
- "(without echoing; leave blank for a random): ").strip()
- if len(newpassword) != 0:
- self.data['password'] = newpassword
-
- def _prompt_server(self):
- """Ask for the server name."""
- newserver = safe_input(
- "Input the server location (the server listens on a single "
- "interface by default) [%s]: " % self.data['server_uri'])
- if newserver != '':
- self.data['server_uri'] = newserver
-
- def _prompt_groups(self):
- """Create the groups.xml file."""
- prompt = '''Input base Operating System for clients:\n'''
- for entry in OS_LIST:
- prompt += "%d: %s\n" % (OS_LIST.index(entry) + 1, entry[0])
- prompt += ': '
- while True:
- try:
- osidx = int(safe_input(prompt))
- self.data['os_sel'] = OS_LIST[osidx - 1][1]
- break
- except ValueError:
- continue
-
- def _prompt_certificate(self):
- """Ask for the key details (country, state, and location)."""
- print("The following questions affect SSL certificate generation.")
- print("If no data is provided, the default values are used.")
- newcountry = safe_input("Country name (2 letter code) for "
- "certificate: ")
- if newcountry != '':
- if len(newcountry) == 2:
- self.data['country'] = newcountry
- else:
- while len(newcountry) != 2:
- newcountry = safe_input("2 letter country code (eg. US): ")
- if len(newcountry) == 2:
- self.data['country'] = newcountry
- break
- else:
- self.data['country'] = 'US'
-
- newstate = safe_input("State or Province Name (full name) for "
- "certificate: ")
- if newstate != '':
- self.data['state'] = newstate
- else:
- self.data['state'] = 'Illinois'
-
- newlocation = safe_input("Locality Name (eg, city) for certificate: ")
- if newlocation != '':
- self.data['location'] = newlocation
- else:
- self.data['location'] = 'Argonne'
-
- def _prompt_keypath(self):
- """ Ask for the key pair location. Try to use sensible
- defaults depending on the OS """
- keypath = safe_input("Path where Bcfg2 server private key will be "
- "created [%s]: " % self.data['keypath'])
- if keypath:
- self.data['keypath'] = keypath
- certpath = safe_input("Path where Bcfg2 server cert will be created "
- "[%s]: " % self.data['certpath'])
- if certpath:
- self.data['certpath'] = certpath
-
- def _init_plugins(self):
- """Initialize each plugin-specific portion of the repository."""
- for plugin in self.plugins:
- if plugin == 'Metadata':
- Bcfg2.Server.Plugins.Metadata.Metadata.init_repo(
- self.data['repopath'],
- groups_xml=GROUPS % self.data['os_sel'],
- clients_xml=CLIENTS % socket.getfqdn())
- else:
- try:
- module = __import__("Bcfg2.Server.Plugins.%s" % plugin, '',
- '', ["Bcfg2.Server.Plugins"])
- cls = getattr(module, plugin)
- cls.init_repo(self.data['repopath'])
- except: # pylint: disable=W0702
- err = sys.exc_info()[1]
- print("Plugin setup for %s failed: %s\n"
- "Check that dependencies are installed" % (plugin,
- err))
-
- def init_repo(self):
- """Setup a new repo and create the content of the
- configuration file."""
- # Create the repository
- path = os.path.join(self.data['repopath'], 'etc')
- try:
- os.makedirs(path)
- self._init_plugins()
- print("Repository created successfuly in %s" %
- self.data['repopath'])
- except OSError:
- print("Failed to create %s." % path)
-
- confdata = CONFIG % (self.data['repopath'],
- ','.join(self.plugins),
- self.data['sendmail'],
- self.data['proto'],
- self.data['password'],
- self.data['certpath'],
- self.data['keypath'],
- self.data['certpath'],
- self.data['server_uri'])
-
- # Create the configuration file and SSL key
- create_conf(self.data['configfile'], confdata)
- create_key(self.data['shostname'], self.data['keypath'],
- self.data['certpath'], self.data['country'],
- self.data['state'], self.data['location'])
diff --git a/src/lib/Bcfg2/Server/Admin/Minestruct.py b/src/lib/Bcfg2/Server/Admin/Minestruct.py
deleted file mode 100644
index 37ca74894..000000000
--- a/src/lib/Bcfg2/Server/Admin/Minestruct.py
+++ /dev/null
@@ -1,56 +0,0 @@
-""" Extract extra entry lists from statistics """
-import getopt
-import lxml.etree
-import sys
-import Bcfg2.Server.Admin
-from Bcfg2.Server.Plugin import PullSource
-
-
-class Minestruct(Bcfg2.Server.Admin.StructureMode):
- """ Extract extra entry lists from statistics """
- __usage__ = ("[options] <client>\n\n"
- " %-25s%s\n"
- " %-25s%s\n" %
- ("-f <filename>", "build a particular file",
- "-g <groups>", "only build config for groups"))
-
- def __call__(self, args):
- if len(args) == 0:
- self.errExit("No argument specified.\n"
- "Please see bcfg2-admin minestruct help for usage.")
- try:
- (opts, args) = getopt.getopt(args, 'f:g:h')
- except getopt.GetoptError:
- self.errExit(self.__doc__)
-
- client = args[0]
- output = sys.stdout
- groups = []
-
- for (opt, optarg) in opts:
- if opt == '-f':
- try:
- output = open(optarg, 'w')
- except IOError:
- self.errExit("Failed to open file: %s" % (optarg))
- elif opt == '-g':
- groups = optarg.split(':')
-
- try:
- extra = set()
- for source in self.bcore.plugins_by_type(PullSource):
- for item in source.GetExtra(client):
- extra.add(item)
- except: # pylint: disable=W0702
- self.errExit("Failed to find extra entry info for client %s" %
- client)
- root = lxml.etree.Element("Base")
- self.log.info("Found %d extra entries" % (len(extra)))
- add_point = root
- for grp in groups:
- add_point = lxml.etree.SubElement(add_point, "Group", name=grp)
- for tag, name in extra:
- self.log.info("%s: %s" % (tag, name))
- lxml.etree.SubElement(add_point, tag, name=name)
-
- lxml.etree.ElementTree(root).write(output, pretty_print=True)
diff --git a/src/lib/Bcfg2/Server/Admin/Perf.py b/src/lib/Bcfg2/Server/Admin/Perf.py
deleted file mode 100644
index 1a772e6fc..000000000
--- a/src/lib/Bcfg2/Server/Admin/Perf.py
+++ /dev/null
@@ -1,38 +0,0 @@
-""" Get performance data from server """
-
-import sys
-import Bcfg2.Options
-import Bcfg2.Client.Proxy
-import Bcfg2.Server.Admin
-
-
-class Perf(Bcfg2.Server.Admin.Mode):
- """ Get performance data from server """
-
- def __call__(self, args):
- output = [('Name', 'Min', 'Max', 'Mean', 'Count')]
- setup = Bcfg2.Options.get_option_parser()
- setup.add_options(dict(ca=Bcfg2.Options.CLIENT_CA,
- certificate=Bcfg2.Options.CLIENT_CERT,
- key=Bcfg2.Options.SERVER_KEY,
- password=Bcfg2.Options.SERVER_PASSWORD,
- server=Bcfg2.Options.SERVER_LOCATION,
- user=Bcfg2.Options.CLIENT_USER,
- timeout=Bcfg2.Options.CLIENT_TIMEOUT))
- opts = sys.argv[1:]
- opts.remove(self.__class__.__name__.lower())
- setup.reparse(argv=opts)
- proxy = Bcfg2.Client.Proxy.ComponentProxy(setup['server'],
- setup['user'],
- setup['password'],
- key=setup['key'],
- cert=setup['certificate'],
- ca=setup['ca'],
- timeout=setup['timeout'])
- data = proxy.get_statistics()
- for key in sorted(data.keys()):
- output.append(
- (key, ) +
- tuple(["%.06f" % item
- for item in data[key][:-1]] + [data[key][-1]]))
- self.print_table(output)
diff --git a/src/lib/Bcfg2/Server/Admin/Pull.py b/src/lib/Bcfg2/Server/Admin/Pull.py
deleted file mode 100644
index 8f84cd87d..000000000
--- a/src/lib/Bcfg2/Server/Admin/Pull.py
+++ /dev/null
@@ -1,147 +0,0 @@
-""" Retrieves entries from clients and integrates the information into
-the repository """
-
-import os
-import sys
-import getopt
-import select
-import Bcfg2.Server.Admin
-from Bcfg2.Server.Plugin import PullSource, Generator
-from Bcfg2.Compat import input # pylint: disable=W0622
-
-
-class Pull(Bcfg2.Server.Admin.MetadataCore):
- """ Retrieves entries from clients and integrates the information
- into the repository """
- __usage__ = ("[options] <client> <entry type> <entry name>\n\n"
- " %-25s%s\n"
- " %-25s%s\n"
- " %-25s%s\n"
- " %-25s%s\n" %
- ("-v", "be verbose",
- "-f", "force",
- "-I", "interactive",
- "-s", "stdin"))
-
- def __init__(self):
- Bcfg2.Server.Admin.MetadataCore.__init__(self)
- self.log = False
- self.mode = 'interactive'
-
- def __call__(self, args):
- use_stdin = False
- try:
- opts, gargs = getopt.getopt(args, 'vfIs')
- except getopt.GetoptError:
- self.errExit(self.__doc__)
- for opt in opts:
- if opt[0] == '-v':
- self.log = True
- elif opt[0] == '-f':
- self.mode = 'force'
- elif opt[0] == '-I':
- self.mode = 'interactive'
- elif opt[0] == '-s':
- use_stdin = True
-
- if use_stdin:
- for line in sys.stdin:
- try:
- self.PullEntry(*line.split(None, 3))
- except SystemExit:
- print(" for %s" % line)
- except:
- print("Bad entry: %s" % line.strip())
- elif len(gargs) < 3:
- self.usage()
- else:
- self.PullEntry(gargs[0], gargs[1], gargs[2])
-
- def BuildNewEntry(self, client, etype, ename):
- """Construct a new full entry for
- given client/entry from statistics.
- """
- new_entry = {'type': etype, 'name': ename}
- pull_sources = self.bcore.plugins_by_type(PullSource)
- for plugin in pull_sources:
- try:
- (owner, group, mode, contents) = \
- plugin.GetCurrentEntry(client, etype, ename)
- break
- except Bcfg2.Server.Plugin.PluginExecutionError:
- if plugin == pull_sources[-1]:
- print("Pull Source failure; could not fetch current state")
- raise SystemExit(1)
-
- try:
- data = {'owner': owner,
- 'group': group,
- 'mode': mode,
- 'text': contents}
- except UnboundLocalError:
- print("Unable to build entry. "
- "Do you have a statistics plugin enabled?")
- raise SystemExit(1)
- for key, val in list(data.items()):
- if val:
- new_entry[key] = val
- return new_entry
-
- def Choose(self, choices):
- """Determine where to put pull data."""
- if self.mode == 'interactive':
- for choice in choices:
- print("Plugin returned choice:")
- if id(choice) == id(choices[0]):
- print("(current entry) ")
- if choice.all:
- print(" => global entry")
- elif choice.group:
- print(" => group entry: %s (prio %d)" %
- (choice.group, choice.prio))
- else:
- print(" => host entry: %s" % (choice.hostname))
-
- # flush input buffer
- while len(select.select([sys.stdin.fileno()], [], [],
- 0.0)[0]) > 0:
- os.read(sys.stdin.fileno(), 4096)
- ans = input("Use this entry? [yN]: ") in ['y', 'Y']
- if ans:
- return choice
- return False
- else:
- # mode == 'force'
- if not choices:
- return False
- return choices[0]
-
- def PullEntry(self, client, etype, ename):
- """Make currently recorded client state correct for entry."""
- new_entry = self.BuildNewEntry(client, etype, ename)
-
- meta = self.bcore.build_metadata(client)
- # Find appropriate plugin in bcore
- glist = [gen for gen in self.bcore.plugins_by_type(Generator)
- if ename in gen.Entries.get(etype, {})]
- if len(glist) != 1:
- self.errExit("Got wrong numbers of matching generators for entry:"
- "%s" % ([g.name for g in glist]))
- plugin = glist[0]
- if not isinstance(plugin, Bcfg2.Server.Plugin.PullTarget):
- self.errExit("Configuration upload not supported by plugin %s" %
- plugin.name)
- try:
- choices = plugin.AcceptChoices(new_entry, meta)
- specific = self.Choose(choices)
- if specific:
- plugin.AcceptPullData(specific, new_entry, self.log)
- except Bcfg2.Server.Plugin.PluginExecutionError:
- self.errExit("Configuration upload not supported by plugin %s" %
- plugin.name)
- # Commit if running under a VCS
- for vcsplugin in list(self.bcore.plugins.values()):
- if isinstance(vcsplugin, Bcfg2.Server.Plugin.Version):
- files = "%s/%s" % (plugin.data, ename)
- comment = 'file "%s" pulled from host %s' % (files, client)
- vcsplugin.commit_data([files], comment)
diff --git a/src/lib/Bcfg2/Server/Admin/Reports.py b/src/lib/Bcfg2/Server/Admin/Reports.py
deleted file mode 100644
index d21d66a22..000000000
--- a/src/lib/Bcfg2/Server/Admin/Reports.py
+++ /dev/null
@@ -1,262 +0,0 @@
-'''Admin interface for dynamic reports'''
-import Bcfg2.Logger
-import Bcfg2.Server.Admin
-import datetime
-import os
-import sys
-import traceback
-from Bcfg2 import settings
-
-# Load django and reports stuff _after_ we know we can load settings
-from django.core import management
-from Bcfg2.Reporting.utils import *
-
-project_directory = os.path.dirname(settings.__file__)
-project_name = os.path.basename(project_directory)
-sys.path.append(os.path.join(project_directory, '..'))
-project_module = __import__(project_name, '', '', [''])
-sys.path.pop()
-
-# Set DJANGO_SETTINGS_MODULE appropriately.
-os.environ['DJANGO_SETTINGS_MODULE'] = '%s.settings' % project_name
-from django.db import transaction
-
-from Bcfg2.Reporting.models import Client, Interaction, \
- Performance, Bundle, Group, FailureEntry, PathEntry, \
- PackageEntry, ServiceEntry, ActionEntry
-
-
-def printStats(fn):
- """
- Print db stats.
-
- Decorator for purging. Prints database statistics after a run.
- """
- def print_stats(self, *data):
- classes = (Client, Interaction, Performance, \
- FailureEntry, ActionEntry, PathEntry, PackageEntry, \
- ServiceEntry, Group, Bundle)
-
- starts = {}
- for cls in classes:
- starts[cls] = cls.objects.count()
-
- fn(self, *data)
-
- for cls in classes:
- print("%s removed: %s" % (cls().__class__.__name__,
- starts[cls] - cls.objects.count()))
-
- return print_stats
-
-
-class Reports(Bcfg2.Server.Admin.Mode):
- """ Manage dynamic reports """
- django_commands = ['dbshell', 'shell', 'sqlall', 'validate']
- __usage__ = ("[command] [options]\n"
- " Commands:\n"
- " init Initialize the database\n"
- " purge Purge records\n"
- " --client [n] Client to operate on\n"
- " --days [n] Records older then n days\n"
- " --expired Expired clients only\n"
- " scrub Scrub the database for duplicate "
- "reasons and orphaned entries\n"
- " stats print database statistics\n"
- " update Apply any updates to the reporting "
- "database\n"
- "\n"
- " Django commands:\n " \
- + "\n ".join(django_commands))
-
- def __init__(self):
- Bcfg2.Server.Admin.Mode.__init__(self)
- try:
- import south
- except ImportError:
- print("Django south is required for Reporting")
- raise SystemExit(-3)
-
- def __call__(self, args):
- if len(args) == 0 or args[0] == '-h':
- self.errExit(self.__usage__)
-
- # FIXME - dry run
-
- if args[0] in self.django_commands:
- self.django_command_proxy(args[0])
- elif args[0] == 'scrub':
- self.scrub()
- elif args[0] == 'stats':
- self.stats()
- elif args[0] in ['init', 'update', 'syncdb']:
- if self.setup['debug']:
- vrb = 2
- elif self.setup['verbose']:
- vrb = 1
- else:
- vrb = 0
- try:
- management.call_command("syncdb", verbosity=vrb)
- management.call_command("migrate", verbosity=vrb)
- except:
- self.errExit("Update failed: %s" % sys.exc_info()[1])
- elif args[0] == 'purge':
- expired = False
- client = None
- maxdate = None
- state = None
- i = 1
- while i < len(args):
- if args[i] == '-c' or args[i] == '--client':
- if client:
- self.errExit("Only one client per run")
- client = args[i + 1]
- print(client)
- i = i + 1
- elif args[i] == '--days':
- if maxdate:
- self.errExit("Max date specified multiple times")
- try:
- maxdate = datetime.datetime.now() - \
- datetime.timedelta(days=int(args[i + 1]))
- except:
- self.errExit("Invalid number of days: %s" %
- args[i + 1])
- i = i + 1
- elif args[i] == '--expired':
- expired = True
- i = i + 1
- if expired:
- if state:
- self.errExit("--state is not valid with --expired")
- self.purge_expired(maxdate)
- else:
- self.purge(client, maxdate, state)
- else:
- self.errExit("Unknown command: %s" % args[0])
-
- @transaction.commit_on_success
- def scrub(self):
- ''' Perform a thorough scrub and cleanup of the database '''
-
- # Cleanup unused entries
- for cls in (Group, Bundle, FailureEntry, ActionEntry, PathEntry,
- PackageEntry, PathEntry):
- try:
- start_count = cls.objects.count()
- cls.prune_orphans()
- self.log.info("Pruned %d %s records" % \
- (start_count - cls.objects.count(), cls.__class__.__name__))
- except:
- print("Failed to prune %s: %s" %
- (cls.__class__.__name__, sys.exc_info()[1]))
-
- def django_command_proxy(self, command):
- '''Call a django command'''
- if command == 'sqlall':
- management.call_command(command, 'Reporting')
- else:
- management.call_command(command)
-
- @printStats
- def purge(self, client=None, maxdate=None, state=None):
- '''Purge historical data from the database'''
-
- filtered = False # indicates whether or not a client should be deleted
-
- if not client and not maxdate and not state:
- self.errExit("Reports.prune: Refusing to prune all data")
-
- ipurge = Interaction.objects
- if client:
- try:
- cobj = Client.objects.get(name=client)
- ipurge = ipurge.filter(client=cobj)
- except Client.DoesNotExist:
- self.errExit("Client %s not in database" % client)
- self.log.debug("Filtering by client: %s" % client)
-
- if maxdate:
- filtered = True
- if not isinstance(maxdate, datetime.datetime):
- raise TypeError("maxdate is not a DateTime object")
- self.log.debug("Filtering by maxdate: %s" % maxdate)
- ipurge = ipurge.filter(timestamp__lt=maxdate)
-
- if settings.DATABASES['default']['ENGINE'] == \
- 'django.db.backends.sqlite3':
- grp_limit = 100
- else:
- grp_limit = 1000
- if state:
- filtered = True
- if state not in ('dirty', 'clean', 'modified'):
- raise TypeError("state is not one of the following values: "
- "dirty, clean, modified")
- self.log.debug("Filtering by state: %s" % state)
- ipurge = ipurge.filter(state=state)
-
- count = ipurge.count()
- rnum = 0
- try:
- while rnum < count:
- grp = list(ipurge[:grp_limit].values("id"))
- # just in case...
- if not grp:
- break
- Interaction.objects.filter(id__in=[x['id']
- for x in grp]).delete()
- rnum += len(grp)
- self.log.debug("Deleted %s of %s" % (rnum, count))
- except:
- self.log.error("Failed to remove interactions")
- (a, b, c) = sys.exc_info()
- msg = traceback.format_exception(a, b, c, limit=2)[-1][:-1]
- del a, b, c
- self.log.error(msg)
-
- # Prune any orphaned ManyToMany relations
- for m2m in (ActionEntry, PackageEntry, PathEntry, ServiceEntry, \
- FailureEntry, Group, Bundle):
- self.log.debug("Pruning any orphaned %s objects" % \
- m2m().__class__.__name__)
- m2m.prune_orphans()
-
- if client and not filtered:
- # Delete the client, ping data is automatic
- try:
- self.log.debug("Purging client %s" % client)
- cobj.delete()
- except:
- self.log.error("Failed to delete client %s" % client)
- (a, b, c) = sys.exc_info()
- msg = traceback.format_exception(a, b, c, limit=2)[-1][:-1]
- del a, b, c
- self.log.error(msg)
-
- @printStats
- def purge_expired(self, maxdate=None):
- '''Purge expired clients from the database'''
-
- if maxdate:
- if not isinstance(maxdate, datetime.datetime):
- raise TypeError("maxdate is not a DateTime object")
- self.log.debug("Filtering by maxdate: %s" % maxdate)
- clients = Client.objects.filter(expiration__lt=maxdate)
- else:
- clients = Client.objects.filter(expiration__isnull=False)
-
- for client in clients:
- self.log.debug("Purging client %s" % client)
- Interaction.objects.filter(client=client).delete()
- client.delete()
-
- def stats(self):
- classes = (Client, Interaction, Performance, \
- FailureEntry, ActionEntry, PathEntry, PackageEntry, \
- ServiceEntry, Group, Bundle)
-
- for cls in classes:
- print("%s has %s records" % (cls().__class__.__name__,
- cls.objects.count()))
diff --git a/src/lib/Bcfg2/Server/Admin/Syncdb.py b/src/lib/Bcfg2/Server/Admin/Syncdb.py
deleted file mode 100644
index 2722364f7..000000000
--- a/src/lib/Bcfg2/Server/Admin/Syncdb.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import sys
-import Bcfg2.settings
-import Bcfg2.Options
-import Bcfg2.Server.Admin
-import Bcfg2.Server.models
-from django.core.exceptions import ImproperlyConfigured
-from django.core.management import setup_environ, call_command
-
-
-class Syncdb(Bcfg2.Server.Admin.Mode):
- """ Sync the Django ORM with the configured database """
-
- def __call__(self, args):
- # Parse options
- setup = Bcfg2.Options.get_option_parser()
- setup.add_option("web_configfile", Bcfg2.Options.WEB_CFILE)
- opts = sys.argv[1:]
- opts.remove(self.__class__.__name__.lower())
- setup.reparse(argv=opts)
-
- setup_environ(Bcfg2.settings)
- Bcfg2.Server.models.load_models(cfile=setup['web_configfile'])
-
- try:
- call_command("syncdb", interactive=False, verbosity=0)
- self._database_available = True
- except ImproperlyConfigured:
- self.errExit("Django configuration problem: %s" %
- sys.exc_info()[1])
- except:
- self.errExit("Database update failed: %s" % sys.exc_info()[1])
diff --git a/src/lib/Bcfg2/Server/Admin/Viz.py b/src/lib/Bcfg2/Server/Admin/Viz.py
deleted file mode 100644
index a29fdaceb..000000000
--- a/src/lib/Bcfg2/Server/Admin/Viz.py
+++ /dev/null
@@ -1,104 +0,0 @@
-""" Produce graphviz diagrams of metadata structures """
-
-import getopt
-import Bcfg2.Server.Admin
-from Bcfg2.Utils import Executor
-
-
-class Viz(Bcfg2.Server.Admin.MetadataCore):
- """ Produce graphviz diagrams of metadata structures """
- __usage__ = ("[options]\n\n"
- " %-32s%s\n"
- " %-32s%s\n"
- " %-32s%s\n"
- " %-32s%s\n"
- " %-32s%s\n" %
- ("-H, --includehosts",
- "include hosts in the viz output",
- "-b, --includebundles",
- "include bundles in the viz output",
- "-k, --includekey",
- "show a key for different digraph shapes",
- "-c, --only-client <clientname>",
- "show only the groups, bundles for the named client",
- "-o, --outfile <file>",
- "write viz output to an output file"))
-
- colors = ['steelblue1', 'chartreuse', 'gold', 'magenta',
- 'indianred1', 'limegreen', 'orange1', 'lightblue2',
- 'green1', 'blue1', 'yellow1', 'darkturquoise', 'gray66']
-
- __plugin_blacklist__ = ['DBStats', 'Cfg', 'Pkgmgr',
- 'Packages', 'Rules', 'Decisions',
- 'Deps', 'Git', 'Svn', 'Fossil', 'Bzr', 'Bundler']
-
- def __call__(self, args):
- # First get options to the 'viz' subcommand
- try:
- opts, args = getopt.getopt(args, 'Hbkc:o:',
- ['includehosts', 'includebundles',
- 'includekey', 'only-client=',
- 'outfile='])
- except getopt.GetoptError:
- self.usage()
-
- hset = False
- bset = False
- kset = False
- only_client = None
- outputfile = False
- for opt, arg in opts:
- if opt in ("-H", "--includehosts"):
- hset = True
- elif opt in ("-b", "--includebundles"):
- bset = True
- elif opt in ("-k", "--includekey"):
- kset = True
- elif opt in ("-c", "--only-client"):
- only_client = arg
- elif opt in ("-o", "--outfile"):
- outputfile = arg
-
- data = self.Visualize(hset, bset, kset, only_client, outputfile)
- if data:
- print(data)
-
- def Visualize(self, hosts=False, bundles=False, key=False,
- only_client=None, output=None):
- """Build visualization of groups file."""
- if output:
- fmt = output.split('.')[-1]
- else:
- fmt = 'png'
-
- exc = Executor()
- cmd = ["dot", "-T", fmt]
- if output:
- cmd.extend(["-o", output])
- idata = ["digraph groups {",
- '\trankdir="LR";',
- self.metadata.viz(hosts, bundles,
- key, only_client, self.colors)]
- if key:
- idata.extend(
- ["\tsubgraph cluster_key {",
- '\tstyle="filled";',
- '\tcolor="lightblue";',
- '\tBundle [ shape="septagon" ];',
- '\tGroup [shape="ellipse"];',
- '\tProfile [style="bold", shape="ellipse"];',
- '\tHblock [label="Host1|Host2|Host3",shape="record"];',
- '\tlabel="Key";',
- "\t}"])
- idata.append("}")
- try:
- result = exc.run(cmd, inputdata=idata)
- except OSError:
- # on some systems (RHEL 6), you cannot run dot with
- # shell=True. on others (Gentoo with Python 2.7), you
- # must. In yet others (RHEL 5), either way works. I have
- # no idea what the difference is, but it's kind of a PITA.
- result = exc.run(cmd, shell=True, inputdata=idata)
- if not result.success:
- print("Error running %s: %s" % (cmd, result.error))
- raise SystemExit(result.retval)
diff --git a/src/lib/Bcfg2/Server/Admin/Xcmd.py b/src/lib/Bcfg2/Server/Admin/Xcmd.py
deleted file mode 100644
index f7f30fd80..000000000
--- a/src/lib/Bcfg2/Server/Admin/Xcmd.py
+++ /dev/null
@@ -1,54 +0,0 @@
-""" XML-RPC Command Interface for bcfg2-admin"""
-
-import sys
-import Bcfg2.Options
-import Bcfg2.Client.Proxy
-import Bcfg2.Server.Admin
-from Bcfg2.Compat import xmlrpclib
-
-
-class Xcmd(Bcfg2.Server.Admin.Mode):
- """ XML-RPC Command Interface """
- __usage__ = "<command>"
-
- def __call__(self, args):
- setup = Bcfg2.Options.get_option_parser()
- setup.add_options(dict(ca=Bcfg2.Options.CLIENT_CA,
- certificate=Bcfg2.Options.CLIENT_CERT,
- key=Bcfg2.Options.SERVER_KEY,
- password=Bcfg2.Options.SERVER_PASSWORD,
- server=Bcfg2.Options.SERVER_LOCATION,
- user=Bcfg2.Options.CLIENT_USER,
- timeout=Bcfg2.Options.CLIENT_TIMEOUT))
- opts = sys.argv[1:]
- opts.remove(self.__class__.__name__.lower())
- setup.reparse(argv=opts)
- Bcfg2.Client.Proxy.RetryMethod.max_retries = 1
- proxy = Bcfg2.Client.Proxy.ComponentProxy(setup['server'],
- setup['user'],
- setup['password'],
- key=setup['key'],
- cert=setup['certificate'],
- ca=setup['ca'],
- timeout=setup['timeout'])
- if len(setup['args']) == 0 or len(args) == 0:
- self.errExit("Usage: xcmd <xmlrpc method> <optional arguments>")
- cmd = args[0]
- try:
- data = getattr(proxy, cmd)(*args[1:])
- except xmlrpclib.Fault:
- flt = sys.exc_info()[1]
- if flt.faultCode == 7:
- print("Unknown method %s" % cmd)
- return
- elif flt.faultCode == 20:
- return
- else:
- raise
- except Bcfg2.Client.Proxy.ProxyError:
- err = sys.exc_info()[1]
- print("Proxy Error: %s" % err)
- return
-
- if data is not None:
- print(data)
diff --git a/src/lib/Bcfg2/Server/Admin/__init__.py b/src/lib/Bcfg2/Server/Admin/__init__.py
deleted file mode 100644
index 06a419354..000000000
--- a/src/lib/Bcfg2/Server/Admin/__init__.py
+++ /dev/null
@@ -1,142 +0,0 @@
-""" Base classes for admin modes """
-
-import re
-import sys
-import logging
-import lxml.etree
-import Bcfg2.Server.Core
-import Bcfg2.Options
-from Bcfg2.Compat import ConfigParser, walk_packages
-
-__all__ = [m[1] for m in walk_packages(path=__path__)]
-
-
-class Mode(object):
- """ Base object for admin modes. Docstrings are used as help
- messages, so if you are seeing this, a help message has not yet
- been added for this mode. """
- __usage__ = None
- __args__ = []
-
- def __init__(self):
- self.setup = Bcfg2.Options.get_option_parser()
- self.configfile = self.setup['configfile']
- self.__cfp = False
- self.log = logging.getLogger('Bcfg2.Server.Admin.Mode')
- usage = "bcfg2-admin %s" % self.__class__.__name__.lower()
- if self.__usage__ is not None:
- usage += " " + self.__usage__
- self.setup.hm = usage
-
- def getCFP(self):
- """ get a config parser for the Bcfg2 config file """
- if not self.__cfp:
- self.__cfp = ConfigParser.ConfigParser()
- self.__cfp.read(self.configfile)
- return self.__cfp
-
- cfp = property(getCFP)
-
- def __call__(self, args):
- raise NotImplementedError
-
- @classmethod
- def usage(cls, rv=1):
- """ Exit with a long usage message """
- print(re.sub(r'\s{2,}', ' ', cls.__doc__.strip()))
- print("")
- print("Usage:")
- usage = "bcfg2-admin %s" % cls.__name__.lower()
- if cls.__usage__ is not None:
- usage += " " + cls.__usage__
- print(" %s" % usage)
- raise SystemExit(rv)
-
- def shutdown(self):
- """ Perform any necessary shtudown tasks for this mode """
- pass
-
- def errExit(self, emsg):
- """ exit with an error """
- print(emsg)
- raise SystemExit(1)
-
- def load_stats(self, client):
- """ Load static statistics from the repository """
- stats = lxml.etree.parse("%s/etc/statistics.xml" % self.setup['repo'])
- hostent = stats.xpath('//Node[@name="%s"]' % client)
- if not hostent:
- self.errExit("Could not find stats for client %s" % (client))
- return hostent[0]
-
- def print_table(self, rows, justify='left', hdr=True, vdelim=" ",
- padding=1):
- """Pretty print a table
-
- rows - list of rows ([[row 1], [row 2], ..., [row n]])
- hdr - if True the first row is treated as a table header
- vdelim - vertical delimiter between columns
- padding - # of spaces around the longest element in the column
- justify - may be left,center,right
-
- """
- hdelim = "="
- justify = {'left': str.ljust,
- 'center': str.center,
- 'right': str.rjust}[justify.lower()]
-
- # Calculate column widths (longest item in each column
- # plus padding on both sides)
- cols = list(zip(*rows))
- col_widths = [max([len(str(item)) + 2 * padding
- for item in col]) for col in cols]
- borderline = vdelim.join([w * hdelim for w in col_widths])
-
- # Print out the table
- print(borderline)
- for row in rows:
- print(vdelim.join([justify(str(item), width)
- for (item, width) in zip(row, col_widths)]))
- if hdr:
- print(borderline)
- hdr = False
-
-
-# pylint wants MetadataCore and StructureMode to be concrete classes
-# and implement __call__, but they aren't and they don't, so we
-# disable that warning
-# pylint: disable=W0223
-
-class MetadataCore(Mode):
- """Base class for admin-modes that handle metadata."""
- __plugin_whitelist__ = None
- __plugin_blacklist__ = None
-
- def __init__(self):
- Mode.__init__(self)
- if self.__plugin_whitelist__ is not None:
- self.setup['plugins'] = [p for p in self.setup['plugins']
- if p in self.__plugin_whitelist__]
- elif self.__plugin_blacklist__ is not None:
- self.setup['plugins'] = [p for p in self.setup['plugins']
- if p not in self.__plugin_blacklist__]
-
- # admin modes don't need to watch for changes. one shot is fine here.
- self.setup['filemonitor'] = 'pseudo'
- try:
- self.bcore = Bcfg2.Server.Core.BaseCore()
- except Bcfg2.Server.Core.CoreInitError:
- msg = sys.exc_info()[1]
- self.errExit("Core load failed: %s" % msg)
- self.bcore.load_plugins()
- self.bcore.fam.handle_event_set()
- self.metadata = self.bcore.metadata
-
- def shutdown(self):
- if hasattr(self, 'bcore'):
- self.bcore.shutdown()
-
-
-class StructureMode(MetadataCore): # pylint: disable=W0223
- """ Base class for admin modes that handle structure plugins """
- pass