summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins/Reporting.py
blob: 883b95ba410501c9c21cf6835f8c69a03dcf0f3e (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
import time
import platform
import traceback
from lxml import etree

from Bcfg2.Reporting.Transport import load_transport_from_config, \
    TransportError, TransportImportError

try:
    import cPickle as pickle
except:
    import pickle

from Bcfg2.Options import REPORTING_COMMON_OPTIONS
from Bcfg2.Server.Plugin import Statistics, PullSource, PluginInitError, \
    PluginExecutionError

def _rpc_call(method):
    def _real_rpc_call(self, *args, **kwargs):
        """Wrapper for calls to the reporting collector"""
        
        try:
            return self.transport.rpc(method, *args, **kwargs)
        except TransportError:
            # this is needed for Admin.Pull
            raise PluginExecutionError
    return _real_rpc_call

class Reporting(Statistics, PullSource):

    __rmi__ = ['Ping', 'GetExtra', 'GetCurrentEntry']

    CLIENT_METADATA_FILEDS = ('profile', 'bundles', 'aliases', 'addresses',
        'groups', 'categories', 'uuid', 'version')

    def __init__(self, core, datastore):
        Statistics.__init__(self, core, datastore)
        PullSource.__init__(self)
        self.core = core
        self.experimental = True

        self.whoami = platform.node()
        self.transport = None

        core.setup.update(REPORTING_COMMON_OPTIONS)
        core.setup.reparse()
        self.logger.error("File limit: %s" % core.setup['reporting_file_limit'])

        try:
            self.transport = load_transport_from_config(core.setup)
        except TransportError:
            self.logger.error("%s: Failed to load transport: %s" %
                (self.name, traceback.format_exc().splitlines()[-1]))
            raise PluginInitError


    def process_statistics(self, client, xdata):
        stats = xdata.find("Statistics")
        stats.set('time', time.asctime(time.localtime()))

        cdata = { 'server': self.whoami }
        for field in self.CLIENT_METADATA_FILEDS:
            try:
                value = getattr(client, field)
            except AttributeError:
                continue
            if value:
                if isinstance(value, set):
                    value = [v for v in value]
                cdata[field] = value

        try:
            interaction_data = pickle.dumps({ 'hostname': client.hostname,
                'metadata': cdata, 'stats':
                etree.tostring(stats, xml_declaration=False).decode('UTF-8') })
        except:
            self.logger.error("%s: Failed to build interaction object: %s" %
                (self.__class__.__name__,
                    traceback.format_exc().splitlines()[-1]))

        # try 3 times to store the data
        for i in [1, 2, 3]:
            try:
                self.transport.store(client.hostname, interaction_data)
                self.logger.debug("%s: Queued statistics data for %s" %
                    (self.__class__.__name__, client.hostname))
                return
            except TransportError:
                continue
            except:
                self.logger.error("%s: Attempt %s: Failed to add statistic %s" %
                    (self.__class__.__name__, i,
                        traceback.format_exc().splitlines()[-1]))
        self.logger.error("%s: Retry limit reached for %s" %
                    (self.__class__.__name__, client.hostname))

    def shutdown(self):
        super(Reporting, self).shutdown()
        if self.transport:
            self.transport.shutdown()

    Ping = _rpc_call('Ping')
    GetExtra = _rpc_call('GetExtra')
    GetCurrentEntry = _rpc_call('GetCurrentEntry')