source: trunk/sara_cmt/sara_cmt/cluster/templatetags/cmt_client.py @ 14141

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

cmts_extras.py -> cmt_client.py:

  • renamed
  • backwards/forwards compatibility with cmts_extras/ramonb_extras

cluster/templatetags/ramonb_extras.py:

  • removed
  • now integrated into cmt_client.py
  • closes #4
File size: 12.2 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
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
208class 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
245class 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')
263def 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
271from django.db.models import get_model
272
273@register.tag(name='getbasenets')
274def 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
283class 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')
327def 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
336class 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')
362def 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
388class 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       
Note: See TracBrowser for help on using the repository browser.