summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Admin/__init__.py
blob: 8f12a940e8ddeecb93dfd9781cdafaac72d77bb6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
""" 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, setup):
        self.setup = setup
        self.configfile = 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__
        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, setup):
        Mode.__init__(self, setup)
        if self.__plugin_whitelist__ is not None:
            setup['plugins'] = [p for p in setup['plugins']
                                if p in self.__plugin_whitelist__]
        elif self.__plugin_blacklist__ is not None:
            setup['plugins'] = [p for p in setup['plugins']
                                if p not in self.__plugin_blacklist__]

        # admin modes don't need to watch for changes.  one shot is fine here.
        setup['filemonitor'] = 'pseudo'
        try:
            self.bcore = Bcfg2.Server.Core.BaseCore(setup)
        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