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: 7162 $"
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
115 return parsestr
116
117 def ressetter(k):
118
119 def setter(v):
120 res[k] = v
121 return setter
122
123 res = {}
124 table = {}
125 basicOptions = (('rra-spec', True, str, None), )
126 for k, required, parser, default in basicOptions:
127 table[k] = strparser(parser), ressetter(k)
128 if not required:
129 res[k] = default
130
131 self.parseFromTable(node, table)
132
133 for k, required, parser, default in basicOptions:
134 if required and k not in res:
135 raise ConfigError('missing required node %s' % k)
136 return res
137
144 return parsestr
145
146 def ressetter(k):
147
148 def setter(v):
149 res[k] = v
150 return setter
151
152 def filename(v):
153 if v[0] != os.sep:
154 raise ConfigError('rrdfile paths should be absolute')
155 return str(v)
156
157 name, = self.parseAttributes(node, ('name', ))
158
159 res = {'name': name}
160 table = {}
161
162 basicOptions = (('manager', True,
163 parsePBConnectionInfo, None),
164 ('component-id', True, str, None),
165 ('ui-state-key', True, str, None),
166 ('sample-frequency', False, int, 300),
167 ('is-gauge', False, common.strToBool, True),
168 ('rrd-ds-spec', False, str, None),
169 ('rrd-file', True, filename, None))
170 for k, required, parser, default in basicOptions:
171 table[k] = strparser(parser), ressetter(k)
172 if not required:
173 res[k] = default
174
175 res['archives'] = []
176 table['archive'] = (self._parseArchive, res['archives'].append)
177
178 self.parseFromTable(node, table)
179
180 for k, required, parser, default in basicOptions:
181 if required and k not in res:
182 raise ConfigError('missing required node %s' % k)
183 if not res['archives']:
184 raise ConfigError('must specify at least one '
185 '<archive> per <source>')
186
187 return res
188
190
191
192
193 root = self.doc.documentElement
194 if not root.nodeName == 'rrdmon':
195 raise ConfigError("unexpected root node: %s" % root.nodeName)
196
197 def strparser(parser):
198
199 def parsestr(node):
200 return self.parseTextNode(node, parser)
201 return parsestr
202
203 def ressetter(k):
204
205 def setter(v):
206 res[k] = v
207 return setter
208
209 res = {'debug': None,
210 'sources': []}
211 table = {'debug': (strparser(str), ressetter('debug')),
212 'source': (self._parseSource, res['sources'].append)}
213
214 self.parseFromTable(root, table)
215
216 if not res['sources']:
217 raise ConfigError('must specify at least one <source>')
218
219 return res
220