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