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

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

templatetags/cmts_extras.py:

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