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

Last change on this file since 14194 was 14194, checked in by sil, 12 years ago

Merged branch 1.0 (until tag 1.0.0) back to trunk

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