1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """
24 RRD monitor configuration parser.
25
26 The format of the configuration file is as follows. *, +, and ? have
27 their normal meanings: 0 or more, 1 or more, and 0 or 1, respectively.
28
29 <rrdmon>
30
31 <!-- normal -->
32 <debug>*:4</debug> ?
33
34 <!-- implementation note: the name of the source is used as the DS
35 name in the RRD file -->
36 <source name="http-streamer"> +
37
38 <!-- how we connect to the manager; parsed with
39 L{flumotion.common.connection.parsePBConnectionInfo} -->
40 <manager>user:test@localhost:7531</manager>
41
42 <!-- the L{flumotion.common.common.componentId} of the component we
43 will poll -->
44 <component-id>/default/http-audio-video</component-id>
45
46 <!-- the key of the L{flumotion.common.componentui} UIState that we
47 will poll; should be numeric in value -->
48 <ui-state-key>stream-totalbytes-raw</ui-state-key>
49
50 <!-- boolean; examples of gauge values would be number of users,
51 temperature, signal strength, precomputed bitrate. The most
52 common non-gauge values are bitrate values, where you poll e.g.
53 the number of bytes sent, not the rate itself -->
54 <is-gauge>False</is-gauge> ?
55
56 <!-- sample frequency in seconds, defaults to 5 minutes -->
57 <sample-frequency>300</sample-frequency> ?
58
59 <!-- Normally we generate the RRD DS spec from the answers above,
60 but if you want to you can specify one directly here. The DS
61 name should be the source name -->
62 <rrd-ds-spec>DS-SPEC</rrd-ds-spec> ?
63
64 <!-- file will be created if necessary -->
65 <rrd-file>/tmp/stream-bitrate.rrd</rrd-file>
66
67 <!-- set of archives to store in the rrd file
68 <archive> +
69 <!-- Would be nice to break this down as we did above for the DS
70 spec, but for now you have to specify the RRA specs manually.
71 Bummer dude! In this example, the meaning is that we should
72 archive a sample every 1*stepsize=1*300s=5 minutes, for 1200
73 samples = 5 min*1200=100h.-->
74 <rra-spec>AVERAGE:0.5:1:1200</rra-spec>
75 </archive>
76 </source>
77
78 </rrdmon>
79 """
80
81 import os
82
83 from flumotion.common import common
84 from flumotion.common.connection import parsePBConnectionInfo
85 from flumotion.common.errors import ConfigError
86 from flumotion.common.fxml import Parser
87
88 __version__ = "$Rev: 6961 $"
89
90
92 """
93 RRD monitor configuration file parser.
94
95 Create a parser via passing the name of the file to parse to
96 __init__. Parse into a dict of properly-typed options by calling
97 parse() on the parser.
98 """
99 parserError = ConfigError
100 logCategory = 'rrdmon-config'
101
103 """
104 @param file: The path to the config file to parse, or a file object
105 @type file: str or file
106 """
107 self.doc = self.getRoot(file)
108
113 return parsestr
114 def ressetter(k):
115 def setter(v):
116 res[k] = v
117 return setter
118
119 res = {}
120 table = {}
121 basicOptions = (('rra-spec', True, str, None),)
122 for k, required, parser, default in basicOptions:
123 table[k] = strparser(parser), ressetter(k)
124 if not required:
125 res[k] = default
126
127 self.parseFromTable(node, table)
128
129 for k, required, parser, default in basicOptions:
130 if required and k not in res:
131 raise ConfigError('missing required node %s' % k)
132 return res
133
138 return parsestr
139 def ressetter(k):
140 def setter(v):
141 res[k] = v
142 return setter
143 def filename(v):
144 if v[0] != os.sep:
145 raise ConfigError('rrdfile paths should be absolute')
146 return str(v)
147
148 name, = self.parseAttributes(node, ('name',))
149
150 res = {'name': name}
151 table = {}
152
153 basicOptions = (('manager', True,
154 parsePBConnectionInfo, None),
155 ('component-id', True, str, None),
156 ('ui-state-key', True, str, None),
157 ('sample-frequency', False, int, 300),
158 ('is-gauge', False, common.strToBool, True),
159 ('rrd-ds-spec', False, str, None),
160 ('rrd-file', True, filename, None))
161 for k, required, parser, default in basicOptions:
162 table[k] = strparser(parser), ressetter(k)
163 if not required:
164 res[k] = default
165
166 res['archives'] = []
167 table['archive'] = (self._parseArchive, res['archives'].append)
168
169 self.parseFromTable(node, table)
170
171 for k, required, parser, default in basicOptions:
172 if required and k not in res:
173 raise ConfigError('missing required node %s' % k)
174 if not res['archives']:
175 raise ConfigError('must specify at least one '
176 '<archive> per <source>')
177
178 return res
179
181
182
183
184 root = self.doc.documentElement
185 if not root.nodeName == 'rrdmon':
186 raise ConfigError("unexpected root node: %s" % root.nodeName)
187
188 def strparser(parser):
189 def parsestr(node):
190 return self.parseTextNode(node, parser)
191 return parsestr
192 def ressetter(k):
193 def setter(v):
194 res[k] = v
195 return setter
196
197 res = {'debug': None,
198 'sources': []}
199 table = {'debug': (strparser(str), ressetter('debug')),
200 'source': (self._parseSource, res['sources'].append)}
201
202 self.parseFromTable(root, table)
203
204 if not res['sources']:
205 raise ConfigError('must specify at least one <source>')
206
207 return res
208