1 | import os, re, string |
---|
2 | |
---|
3 | # Inspired by Django tips on: |
---|
4 | # http://www.b-list.org/weblog/2006/jun/07/django-tips-write-better-template-tags/ |
---|
5 | from django import template |
---|
6 | from django.template.defaultfilters import stringfilter |
---|
7 | from django.utils.encoding import smart_unicode, force_unicode |
---|
8 | |
---|
9 | from sara_cmt.logger import Logger |
---|
10 | logger = Logger().getLogger() |
---|
11 | |
---|
12 | #from django.db.models import get_model |
---|
13 | |
---|
14 | register = template.Library() |
---|
15 | |
---|
16 | class NoBlankLinesNode(template.Node): |
---|
17 | """ |
---|
18 | Renderer that'll remove all blank lines. |
---|
19 | """ |
---|
20 | |
---|
21 | def __init__(self, nodelist): |
---|
22 | self.nodelist = nodelist |
---|
23 | |
---|
24 | def render(self, context): |
---|
25 | return re.sub('\n([\ \t]*\n)+', '\n', force_unicode( |
---|
26 | self.nodelist.render(context))) |
---|
27 | |
---|
28 | @register.tag |
---|
29 | def noblanklines(parser, token): |
---|
30 | nodelist = parser.parse(('endnoblanklines',)) |
---|
31 | parser.delete_first_token() |
---|
32 | return NoBlankLinesNode(nodelist) |
---|
33 | |
---|
34 | @stringfilter |
---|
35 | def arpanize(value): |
---|
36 | """ |
---|
37 | Converts a IP (range) to reversed DNS style arpa notation |
---|
38 | |
---|
39 | Usage: |
---|
40 | {{{ <variable>|arpanize }}} |
---|
41 | |
---|
42 | I.e.: |
---|
43 | {% assign broadcast = '192.168.1.0' %} |
---|
44 | {{{ broastcast|arpanize }}} |
---|
45 | Results in output: |
---|
46 | 1.168.192.in-addr.arpa |
---|
47 | """ |
---|
48 | ip_blocks = value.split('.') |
---|
49 | |
---|
50 | reverse_block = [ ip_blocks[2], ip_blocks[1], ip_blocks[0], 'in-addr.arpa' ] |
---|
51 | |
---|
52 | return string.join( reverse_block, '.' ) |
---|
53 | |
---|
54 | register.filter( 'arpanize', arpanize ) |
---|
55 | |
---|
56 | @stringfilter |
---|
57 | def base_net(value): |
---|
58 | """ |
---|
59 | Converts a IP (range) to it's first 3 octects |
---|
60 | |
---|
61 | Usage: |
---|
62 | {{{ <variable>|base_net }}} |
---|
63 | |
---|
64 | I.e.: |
---|
65 | {% assign broadcast = '192.168.1.0' %} |
---|
66 | {{{ broastcast|base_net }}} |
---|
67 | Results in output: |
---|
68 | 192.168.1 |
---|
69 | """ |
---|
70 | ip_blocks = value.split('.') |
---|
71 | |
---|
72 | return string.join( ip_blocks[:3], '.' ) |
---|
73 | |
---|
74 | register.filter( 'base_net', base_net ) |
---|
75 | |
---|
76 | @stringfilter |
---|
77 | def ip_last_digit(value): |
---|
78 | """ |
---|
79 | Converts a IP (range) to it's last octect |
---|
80 | |
---|
81 | Usage: |
---|
82 | {{{ <variable>|ip_last_digit }}} |
---|
83 | |
---|
84 | I.e.: |
---|
85 | {% assign myip = '192.168.1.123' %} |
---|
86 | {{{ myip|ip_last_digit }}} |
---|
87 | Results in output: |
---|
88 | 123 |
---|
89 | """ |
---|
90 | ip_blocks = value.split('.') |
---|
91 | |
---|
92 | return ip_blocks[3] |
---|
93 | |
---|
94 | register.filter( 'ip_last_digit', ip_last_digit ) |
---|
95 | |
---|
96 | @register.tag(name='assign') |
---|
97 | def do_assign(parser,token): |
---|
98 | |
---|
99 | """ |
---|
100 | Variable assignment within template |
---|
101 | |
---|
102 | Usage: {% assign newvar = <space seperated list of strings/vars> %} |
---|
103 | i.e.: {% assign file_name = '/var/tmp/rack-' rack.label '.txt' %} |
---|
104 | """ |
---|
105 | definition = token.split_contents() |
---|
106 | |
---|
107 | if len(definition) < 4: |
---|
108 | raise template.TemplateSyntaxError, '%r tag requires at least 4 arguments' % tag |
---|
109 | |
---|
110 | tag = definition[0] |
---|
111 | new_var = definition[1] |
---|
112 | is_teken = definition[2] |
---|
113 | assignees = definition[3:] |
---|
114 | |
---|
115 | return resolveVariables( new_var, assignees ) |
---|
116 | |
---|
117 | class resolveVariables(template.Node): |
---|
118 | |
---|
119 | def __init__(self, varname, varlist ): |
---|
120 | |
---|
121 | self.varname = varname |
---|
122 | self.varlist = varlist |
---|
123 | |
---|
124 | def render(self, context): |
---|
125 | resvars = [ ] |
---|
126 | |
---|
127 | for a in self.varlist: |
---|
128 | |
---|
129 | var_str = '' |
---|
130 | |
---|
131 | if not (a[0] == a[-1] and a[0] in ('"', "'")): |
---|
132 | try: |
---|
133 | # RB: no quotes must mean its a variable |
---|
134 | # |
---|
135 | a_var = template.Variable( a ) |
---|
136 | var_str = a_var.resolve(context) |
---|
137 | |
---|
138 | except template.VariableDoesNotExist: |
---|
139 | |
---|
140 | #RB: still think not allowed to raise exceptions from render function |
---|
141 | # |
---|
142 | #raise template.TemplateSyntaxError, 'cannot resolve variable %r' %( str( self.path ) ) |
---|
143 | pass |
---|
144 | |
---|
145 | resvars.append( str(var_str) ) |
---|
146 | |
---|
147 | else: |
---|
148 | #RB: assume strings are quoted |
---|
149 | #RB: strip quotes from string |
---|
150 | # |
---|
151 | a = str( a.strip("'").strip('"') ) |
---|
152 | resvars.append( str(a) ) |
---|
153 | |
---|
154 | #RB: finally assign the concatenated string to new varname |
---|
155 | context[ self.varname ] = string.join( resvars, '' ) |
---|
156 | |
---|
157 | #RB: Django render functions not supposed/allowed to raise Exception, I think |
---|
158 | return '' |
---|
159 | |
---|
160 | @register.tag(name='store') |
---|
161 | def do_save_meta(parser, token): |
---|
162 | """ |
---|
163 | Compilation function to use for meta-info. |
---|
164 | |
---|
165 | Usage: {% store '/path/to/file' %} |
---|
166 | {% store variable %} # variable = '/path/to/file' |
---|
167 | """ |
---|
168 | definition = token.split_contents() |
---|
169 | |
---|
170 | if len(definition) != 4 and len(definition) != 2: |
---|
171 | raise template.TemplateSyntaxError, '%r tag requires at least 1 arguments' % tag |
---|
172 | |
---|
173 | tag = definition[0] |
---|
174 | path_arg = definition[1] |
---|
175 | |
---|
176 | if len(definition) == 4: |
---|
177 | |
---|
178 | #RB: OLDSTYLE |
---|
179 | #RB: 4 arguments means: {% store /path/filename as output %} |
---|
180 | #RB: old style: DONT try to resolve variable |
---|
181 | #RB: instead convert filename to quoted string |
---|
182 | |
---|
183 | kw_as = definition[2] |
---|
184 | kw_output_name = definition[3] |
---|
185 | |
---|
186 | path_str = "'%s'" %path_arg |
---|
187 | |
---|
188 | # RB: parse the entire template for old-style |
---|
189 | nodelist = parser.parse() |
---|
190 | |
---|
191 | else: |
---|
192 | #RB: NEWSTYLE |
---|
193 | #RB: 2 arguments can mean: {% store 'string' %} |
---|
194 | #RB: 2 arguments can mean: {% store variable %} |
---|
195 | |
---|
196 | kw_output_name = None |
---|
197 | |
---|
198 | path_str = path_arg |
---|
199 | |
---|
200 | # RB: parse the template thing until %endstore found |
---|
201 | nodelist = parser.parse(('endstore',)) |
---|
202 | # RB: throw away %endstore tag |
---|
203 | parser.delete_first_token() |
---|
204 | |
---|
205 | # RB: Now lets start writing output files |
---|
206 | return generateStoreOutput(tag, path_str, nodelist, kw_output_name) |
---|
207 | |
---|
208 | class generateStoreOutput(template.Node): |
---|
209 | |
---|
210 | def __init__(self, tag, path_str, nodelist, kw_output_name=None): |
---|
211 | self.tag = tag |
---|
212 | self.nodelist = nodelist |
---|
213 | self.path_str = path_str |
---|
214 | self.kw_output_name = kw_output_name |
---|
215 | |
---|
216 | def render(self, context): |
---|
217 | |
---|
218 | if (self.path_str[0] == self.path_str[-1] and self.path_str[0] in ('"', "'")): |
---|
219 | |
---|
220 | mypath_str = str(self.path_str)[1:-1] |
---|
221 | |
---|
222 | else: |
---|
223 | # RB: Not quoted: must be a variable: attempt to resolve to value |
---|
224 | try: |
---|
225 | pathvar = template.Variable( str(self.path_str) ) |
---|
226 | mypath_str = pathvar.resolve(context) |
---|
227 | except template.VariableDoesNotExist: |
---|
228 | #raise template.TemplateSyntaxError, '%r tag argument 1: not a variable %r' %( tag, path_str ) |
---|
229 | pass |
---|
230 | |
---|
231 | if self.kw_output_name: |
---|
232 | # RB: store 'output' variable filename for BW compat |
---|
233 | |
---|
234 | context[ self.kw_output_name ] = mypath_str |
---|
235 | |
---|
236 | # RB: render template between store tags |
---|
237 | output = self.nodelist.render(context) |
---|
238 | |
---|
239 | # RB: store output in context dict for later writing to file |
---|
240 | context['stores'][ mypath_str ] = output |
---|
241 | |
---|
242 | # RB: output generated into context dict, so we return nothing |
---|
243 | return '' |
---|
244 | |
---|
245 | class ScriptNode(template.Node): |
---|
246 | """ |
---|
247 | Renderer, which executes the lines included in the script-tags. |
---|
248 | """ |
---|
249 | |
---|
250 | def __init__(self, nodelist): |
---|
251 | self.nodelist = nodelist |
---|
252 | |
---|
253 | def render(self, context): |
---|
254 | script = self.nodelist.render(context) |
---|
255 | if context.has_key('epilogue'): |
---|
256 | context['epilogue'].append(script) |
---|
257 | else: |
---|
258 | context['epilogue'] = [script] |
---|
259 | # All content between {% epilogue %} and {% endepilogue %} is parsed now |
---|
260 | return '' |
---|
261 | |
---|
262 | @register.tag(name='epilogue') |
---|
263 | def do_epilogue(parser, token): |
---|
264 | """ |
---|
265 | Saving the contents between the epilogue-tags |
---|
266 | """ |
---|
267 | nodelist = parser.parse(('endepilogue',)) |
---|
268 | parser.delete_first_token() |
---|
269 | return ScriptNode(nodelist) |
---|
270 | |
---|
271 | from django.db.models import get_model |
---|
272 | |
---|
273 | @register.tag(name='getbasenets') |
---|
274 | def do_getbasenets(parser, token): |
---|
275 | |
---|
276 | try: |
---|
277 | tag, network_name, kw_as, varname = token.split_contents() |
---|
278 | except ValueError: |
---|
279 | raise template.TemplateSyntaxError, '%r tag requires exactly 4 arguments' % tag |
---|
280 | |
---|
281 | return getBaseNets( varname, network_name ) |
---|
282 | |
---|
283 | class getBaseNets(template.Node): |
---|
284 | |
---|
285 | """ |
---|
286 | Get list of basenets in a network (name) |
---|
287 | |
---|
288 | Usage: {% getbasenets <network name> as <listname> %} |
---|
289 | """ |
---|
290 | |
---|
291 | def __init__(self, varname, network_name ): |
---|
292 | |
---|
293 | self.varname = varname |
---|
294 | self.network_name = network_name.strip("'").strip('"').__str__() |
---|
295 | self.basenets = [ ] |
---|
296 | |
---|
297 | def render(self, context): |
---|
298 | |
---|
299 | if (self.network_name[0] == self.network_name[-1] and self.network_name[0] in ('"', "'")): |
---|
300 | |
---|
301 | network_str = str( self.network_name.strip("'").strip('"') ) |
---|
302 | else: |
---|
303 | # RB: Not quoted: must be a variable: attempt to resolve to value |
---|
304 | try: |
---|
305 | networkvar = template.Variable( str(self.network_name) ) |
---|
306 | network_str = networkvar.resolve(context) |
---|
307 | except template.VariableDoesNotExist: |
---|
308 | #raise template.TemplateSyntaxError, '%r tag argument 1: not a variable %r' %( tag, path_str ) |
---|
309 | pass |
---|
310 | |
---|
311 | from IPy import IP |
---|
312 | |
---|
313 | network_units = get_model('cluster', 'Network').objects.filter( name=str(network_str) ) |
---|
314 | |
---|
315 | for n in network_units: |
---|
316 | |
---|
317 | for ipnum in IP( n.cidr ): |
---|
318 | if not base_net( ipnum ) in self.basenets: |
---|
319 | self.basenets.append( str( base_net( ipnum ) ) ) |
---|
320 | |
---|
321 | context[ self.varname ] = self.basenets |
---|
322 | self.basenets = [ ] |
---|
323 | |
---|
324 | return '' |
---|
325 | |
---|
326 | @register.tag(name='getracks') |
---|
327 | def do_getracks(parser, token): |
---|
328 | |
---|
329 | try: |
---|
330 | tag, cluster, kw_as, name = token.split_contents() |
---|
331 | except ValueError: |
---|
332 | raise template.TemplateSyntaxError, '%r tag requires exactly 4 arguments' % tag |
---|
333 | |
---|
334 | return getRacks( name, cluster ) |
---|
335 | |
---|
336 | class getRacks(template.Node): |
---|
337 | |
---|
338 | """ |
---|
339 | Get list of racks in a cluster |
---|
340 | |
---|
341 | Usage: {% getracks <cluster> as <listname> %} |
---|
342 | """ |
---|
343 | |
---|
344 | def __init__(self, name, cluster): |
---|
345 | |
---|
346 | self.name = name |
---|
347 | self.cluster = cluster.strip("'").strip('"').__str__() |
---|
348 | self.racks = [ ] |
---|
349 | |
---|
350 | def render(self, context): |
---|
351 | |
---|
352 | cluster_units = get_model('cluster', 'HardwareUnit').objects.filter( cluster__name=str(self.cluster) ) |
---|
353 | |
---|
354 | for u in cluster_units: |
---|
355 | if u.rack not in self.racks: |
---|
356 | self.racks.append( u.rack ) |
---|
357 | |
---|
358 | context[ self.name ] = self.racks |
---|
359 | return '' |
---|
360 | |
---|
361 | @register.tag(name='use') |
---|
362 | def do_use(parser, token): |
---|
363 | """ |
---|
364 | Compilation function to definine Querysets for later use. |
---|
365 | |
---|
366 | Usage: {% use <entity> with <attribute>=<value> as <list/var> <key> %} |
---|
367 | """ |
---|
368 | tag = token.contents.split()[0] |
---|
369 | |
---|
370 | try: |
---|
371 | definition = token.split_contents() |
---|
372 | # definition should look like ['use', <entity>, 'with' <query>, 'as', '<key>'] |
---|
373 | except ValueError: |
---|
374 | raise template.TemplateSyntaxError, '%r tag requires at least 5 arguments' % tag |
---|
375 | if len(definition) != 6: |
---|
376 | raise template.TemplateSyntaxError, '%r tag requires at least 5 arguments' % tag |
---|
377 | if definition[2] != 'with': |
---|
378 | raise template.TemplateSyntaxError, "second argument of %r tag has to be 'with'" % tag |
---|
379 | if definition[-2] != 'as': |
---|
380 | raise template.TemplateSyntaxError, "second last argument of %r tag has to be 'as'" % tag |
---|
381 | |
---|
382 | entity = definition[1] |
---|
383 | query = definition[-3] |
---|
384 | key = definition[-1] |
---|
385 | |
---|
386 | return QuerySetNode(entity, query, key) |
---|
387 | |
---|
388 | class QuerySetNode(template.Node): |
---|
389 | """ |
---|
390 | Renderer, which fetches objects from the database. |
---|
391 | """ |
---|
392 | |
---|
393 | def __init__(self, entity, query, key): |
---|
394 | self.entity = entity |
---|
395 | self.query = query |
---|
396 | self.key = key |
---|
397 | |
---|
398 | def render(self, context): |
---|
399 | |
---|
400 | if (self.query[0] == self.query[-1] and self.query[0] in ('"', "'")): |
---|
401 | |
---|
402 | myquery_str = str( self.query.strip("'").strip('"') ) |
---|
403 | else: |
---|
404 | # RB: Not quoted: must be a variable: attempt to resolve to value |
---|
405 | try: |
---|
406 | queryvar = template.Variable( str(self.query) ) |
---|
407 | myquery_str = queryvar.resolve(context) |
---|
408 | except template.VariableDoesNotExist: |
---|
409 | #raise template.TemplateSyntaxError, '%r tag argument 1: not a variable %r' %( tag, path_str ) |
---|
410 | pass |
---|
411 | |
---|
412 | attr, val = myquery_str.split('=') |
---|
413 | queryset = get_model('cluster', self.entity).objects.filter(**{attr:val}) |
---|
414 | if len(queryset) == 1: |
---|
415 | queryset = queryset[0] |
---|
416 | context[self.key] = queryset |
---|
417 | logger.debug('context = %s'%context) |
---|
418 | return '' |
---|
419 | |
---|
420 | # use <entity> with <attribute>=<value> as <key> |
---|
421 | |
---|
422 | |
---|
423 | |
---|