source: trunk/sara_cmt/sara_cmt/cluster/templatetags/cmts_extras.py @ 14138

Last change on this file since 14138 was 14138, checked in by ramonb, 12 years ago

templatetags/cmts_extras.py:

  • parse entire file if oldstyle %store
  • parse until %endstore for newstyle %store
  • generateStoreOutput(): set 'output' variable in context for oldstyle %store
  • see #4
File size: 12.3 KB
Line 
1import 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/
5from django import template
6from django.template.defaultfilters import stringfilter
7from django.utils.encoding import smart_unicode, force_unicode
8
9from sara_cmt.logger import Logger
10logger = Logger().getLogger()
11
12#from django.db.models import get_model
13
14register = template.Library()
15
16class 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
29def noblanklines(parser, token):
30    nodelist = parser.parse(('endnoblanklines',))
31    parser.delete_first_token()
32    return NoBlankLinesNode(nodelist)
33
34@stringfilter
35def 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
54register.filter( 'arpanize', arpanize )
55
56@stringfilter
57def 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
74register.filter( 'base_net', base_net )
75
76@stringfilter
77def 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
94register.filter( 'ip_last_digit', ip_last_digit )
95
96@register.tag(name='assign')
97def 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
117class 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')
161def 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    #kw_as = definition[2]
176    #kw_output = definition[3]
177
178    if len(definition) == 4:
179
180        #RB: OLDSTYLE
181        #RB: 4 arguments means: {% store /path/filename as output %}
182        #RB: old style: DONT try to resolve variable
183        #RB: instead convert filename to quoted string
184
185        path_str = "'%s'" %path_arg
186
187        # RB: parse the entire template for old-style
188        nodelist = parser.parse()
189
190        # RB: set backwards compatibility for StoreOutput
191        bw_compat = True
192
193    else:
194        #RB: NEWSTYLE
195        #RB: 2 arguments can mean: {% store 'string' %}
196        #RB: 2 arguments can mean: {% store variable %}
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: no backwards compatibility for StoreOutput
206        bw_compat = False
207
208    # RB: Now lets start writing output files
209    return generateStoreOutput(tag, path_str, nodelist, bw_compat)
210
211class generateStoreOutput(template.Node):
212
213    def __init__(self, tag, path_str, nodelist, bw_compat=False):
214        self.tag = tag
215        self.nodelist = nodelist
216        self.path_str = path_str
217        self.bw_compat = bw_compat
218
219    def render(self, context):
220
221        if (self.path_str[0] == self.path_str[-1] and self.path_str[0] in ('"', "'")):
222
223            mypath_str = str(self.path_str)[1:-1]
224
225        else:
226            # RB: Not quoted: must be a variable: attempt to resolve to value
227            try:
228                pathvar = template.Variable( str(self.path_str) )
229                mypath_str = pathvar.resolve(context)
230            except template.VariableDoesNotExist:
231                #raise template.TemplateSyntaxError, '%r tag argument 1: not a variable %r' %( tag, path_str )
232                pass
233
234        if self.bw_compat:
235            # RB: store 'output' variable filename for BW compat
236
237            context[ 'output' ] = mypath_str
238
239        # RB: render template between store tags
240        output = self.nodelist.render(context)
241
242        # RB: store output in context dict for later writing to file
243        context['stores'][ mypath_str ] = output
244
245        # RB: output generated into context dict, so we return nothing
246        return ''
247
248class ScriptNode(template.Node):
249    """
250        Renderer, which executes the lines included in the script-tags.
251    """
252
253    def __init__(self, nodelist):
254        self.nodelist = nodelist
255
256    def render(self, context):
257        script = self.nodelist.render(context)
258        if context.has_key('epilogue'):
259            context['epilogue'].append(script)
260        else:
261            context['epilogue'] = [script]
262        # All content between {% epilogue %} and {% endepilogue %} is parsed now
263        return ''
264
265@register.tag(name='epilogue')
266def do_epilogue(parser, token):
267    """
268        Saving the contents between the epilogue-tags
269    """
270    nodelist = parser.parse(('endepilogue',))
271    parser.delete_first_token()
272    return ScriptNode(nodelist)
273
274from django.db.models import get_model
275
276@register.tag(name='getbasenets')
277def do_getbasenets(parser, token):
278
279    try:
280        tag, network_name, kw_as, varname = token.split_contents()
281    except ValueError:
282        raise template.TemplateSyntaxError, '%r tag requires exactly 4 arguments' % tag
283
284    return getBaseNets( varname, network_name )
285
286class getBaseNets(template.Node):
287
288    """
289        Get list of basenets in a network (name)
290
291        Usage: {% getbasenets <network name> as <listname> %}
292    """
293
294    def __init__(self, varname, network_name ):
295
296        self.varname = varname
297        self.network_name = network_name.strip("'").strip('"').__str__()
298        self.basenets = [ ]
299
300    def render(self, context):
301
302        if (self.network_name[0] == self.network_name[-1] and self.network_name[0] in ('"', "'")):
303
304            network_str = str( self.network_name.strip("'").strip('"') )
305        else:
306            # RB: Not quoted: must be a variable: attempt to resolve to value
307            try:
308                networkvar = template.Variable( str(self.network_name) )
309                network_str = networkvar.resolve(context)
310            except template.VariableDoesNotExist:
311                #raise template.TemplateSyntaxError, '%r tag argument 1: not a variable %r' %( tag, path_str )
312                pass
313
314        from IPy import IP
315
316        network_units = get_model('cluster', 'Network').objects.filter( name=str(network_str) )
317
318        for n in network_units:
319
320            for ipnum in IP( n.cidr ):
321                if not base_net( ipnum ) in self.basenets:
322                    self.basenets.append( str( base_net( ipnum ) ) )
323
324        context[ self.varname ] = self.basenets
325        self.basenets = [ ]
326
327        return ''
328
329@register.tag(name='getracks')
330def do_getracks(parser, token):
331
332    try:
333        tag, cluster, kw_as, name = token.split_contents()
334    except ValueError:
335        raise template.TemplateSyntaxError, '%r tag requires exactly 4 arguments' % tag
336
337    return getRacks( name, cluster )
338
339class getRacks(template.Node):
340
341    """
342        Get list of racks in a cluster
343
344        Usage: {% getracks <cluster> as <listname> %}
345    """
346
347    def __init__(self, name, cluster):
348
349        self.name = name
350        self.cluster = cluster.strip("'").strip('"').__str__()
351        self.racks = [ ]
352
353    def render(self, context):
354
355        cluster_units = get_model('cluster', 'HardwareUnit').objects.filter( cluster__name=str(self.cluster) )
356
357        for u in cluster_units:
358            if u.rack not in self.racks:
359                self.racks.append( u.rack )
360
361        context[ self.name ] = self.racks
362        return ''
363
364@register.tag(name='use')
365def do_use(parser, token):
366    """
367        Compilation function to definine Querysets for later use.
368       
369        Usage: {% use <entity> with <attribute>=<value> as <list/var> <key> %}
370    """
371    tag = token.contents.split()[0]
372
373    try:
374        definition = token.split_contents()
375        # definition should look like ['use', <entity>, 'with' <query>, 'as', '<key>']
376    except ValueError:
377        raise template.TemplateSyntaxError, '%r tag requires at least 5 arguments' % tag
378    if len(definition) != 6:
379        raise template.TemplateSyntaxError, '%r tag requires at least 5 arguments' % tag
380    if definition[2] != 'with':
381        raise template.TemplateSyntaxError, "second argument of %r tag has to be 'with'" % tag
382    if definition[-2] != 'as':
383        raise template.TemplateSyntaxError, "second last argument of %r tag has to be 'as'" % tag
384
385    entity = definition[1]
386    query = definition[-3]
387    key = definition[-1]
388
389    return QuerySetNode(entity, query, key)
390
391class QuerySetNode(template.Node):
392    """
393        Renderer, which fetches objects from the database.
394    """
395
396    def __init__(self, entity, query, key):
397        self.entity = entity
398        self.query = query
399        self.key = key
400
401    def render(self, context):
402
403        if (self.query[0] == self.query[-1] and self.query[0] in ('"', "'")):
404
405            myquery_str = str( self.query.strip("'").strip('"') )
406        else:
407            # RB: Not quoted: must be a variable: attempt to resolve to value
408            try:
409                queryvar = template.Variable( str(self.query) )
410                myquery_str = queryvar.resolve(context)
411            except template.VariableDoesNotExist:
412                #raise template.TemplateSyntaxError, '%r tag argument 1: not a variable %r' %( tag, path_str )
413                pass
414
415        attr, val = myquery_str.split('=')
416        queryset = get_model('cluster', self.entity).objects.filter(**{attr:val})
417        if len(queryset) == 1:
418            queryset = queryset[0]
419        context[self.key] = queryset
420        logger.debug('context = %s'%context)
421        return ''
422
423# use <entity> with <attribute>=<value> as <key>
424
425
426       
Note: See TracBrowser for help on using the repository browser.