source: fedd/federation/topdl.py @ ae0f69a

axis_examplecompt_changesinfo-opsversion-2.00version-3.01version-3.02
Last change on this file since ae0f69a was 69692a9, checked in by Ted Faber <faber@…>, 15 years ago

Looks like Dragon is being called correctly. Internals remain a bit messy.

  • Property mode set to 100644
File size: 20.2 KB
Line 
1#!/usr/local/bin/python
2
3import re
4
5class base:
6    @staticmethod
7    def init_class(c, arg):
8        if isinstance(arg, dict):
9            try:
10                return c(**arg)
11            except:
12                print "%s" % arg
13                raise
14        elif isinstance(arg, c):
15            return arg
16        else:
17            return None
18
19    @staticmethod
20    def make_list(a):
21        if isinstance(a, basestring) or isinstance(a, dict): return [ a ]
22        elif getattr(a, '__iter__', None): return a
23        else: return [ a ]
24
25    def remove_attribute(self, key):
26        to_del = None
27        attrs = getattr(self, 'attribute', [])
28        for i, a in enumerate(attrs):
29            if a.attribute == key:
30                to_del = i
31                break
32       
33        if to_del: del attrs[i]
34
35    def get_attribute(self, key):
36        rv = None
37        attrs = getattr(self, 'attribute', None)
38        if attrs:
39            for a in attrs:
40                if a.attribute == key:
41                    rv = a.value
42                    break
43        return rv
44
45    def set_attribute(self, key, value):
46        attrs = getattr(self, 'attribute', None)
47        if attrs is None:
48            return
49        for a in attrs:
50            if a.attribute == key: 
51                a.value = value
52                break
53        else:
54            attrs.append(Attribute(key, value))
55
56class ConsistencyError(RuntimeError): pass
57
58class Attribute(base):
59    def __init__(self, attribute, value):
60        self.attribute = attribute
61        self.value = value
62
63    def clone(self):
64        return Attribute(attribute=self.attribute, value=self.value)
65
66    def to_dict(self):
67        return { 'attribute': self.attribute, 'value': self.value }
68
69class Capacity(base):
70    def __init__(self, rate, kind):
71        self.rate = float(rate)
72        self.kind = kind
73
74    def clone(self):
75        return Capacity(rate=self.rate, kind=self.kind)
76
77    def to_dict(self):
78        return { 'rate': float(self.rate), 'kind': self.kind }
79
80class Latency(base):
81    def __init__(self, time, kind):
82        self.time = float(time)
83        self.kind = kind
84
85    def clone(self):
86        return Latency(time=self.time, kind=self.kind)
87
88    def to_dict(self):
89        return { 'time': float(self.time), 'kind': self.kind }
90
91class Substrate(base):
92    def __init__(self, name, capacity=None, latency=None, attribute=[]):
93        self.name = name
94        self.capacity = self.init_class(Capacity, capacity)
95        self.latency = self.init_class(Latency, latency)
96        self.attribute = [ self.init_class(Attribute, a) \
97                for a in self.make_list(attribute) ]
98        self.interfaces = [ ]
99
100    def clone(self):
101        if self.capacity: c = self.capacity.clone()
102        else: c = None
103
104        if self.latency: l = self.latency.clone()
105        else: l = None
106
107        return Substrate(name=self.name,
108                capacity=c,
109                latency=l,
110                attribute = [a.clone() for a in self.attribute])
111
112    def to_dict(self):
113        rv = { 'name': self.name }
114        if self.capacity:
115            rv['capacity'] = self.capacity.to_dict()
116        if self.latency:
117            rv['latency'] = self.latency.to_dict()
118        if self.attribute:
119            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
120        return rv
121
122class CPU(base):
123    def __init__(self, type, attribute=[]):
124        self.type = type
125        self.attribute = [ self.init_class(Attribute, a) for a in \
126                self.make_list(attribute) ]
127
128    def clone(self):
129        return CPU(type=self.type,
130                attribute = [a.clone() for a in self.attribute])
131
132    def to_dict(self):
133        rv = { 'type': self.type}
134        if self.attribute:
135            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
136        return rv
137
138class Storage(base):
139    def __init__(self, amount, persistence, attribute=[]):
140        self.amount = float(amount)
141        self.presistence = persistence
142        self.attribute = [ self.init_class(Attribute, a) \
143                for a in self.make_list(attribute) ]
144
145    def clone(self):
146        return Storage(amount=self.amount, persistence=self.persistence, 
147                attribute = [a.clone() for a in self.attribute])
148
149    def to_dict(self):
150        rv = { 'amount': float(self.amount), 'persistence': self.persistence }
151        if self.attribute:
152            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
153        return rv
154
155class OperatingSystem(base):
156    def __init__(self, name=None, version=None, distribution=None,
157            distributionversion=None, attribute=[]):
158        self.name = name
159        self.version = version
160        self.distribution = distribution
161        self.distributionversion = distributionversion
162        self.attribute = [ self.init_class(Attribute, a) \
163                for a in self.make_list(attribute) ]
164
165    def clone(self):
166        return OperatingSystem(name=self.name,
167                version=self.version,
168                distribution=self.distribution,
169                distributionversion=self.distributionversion,
170                attribute = [ a.clone() for a in self.attribute])
171
172    def to_dict(self):
173        rv = { }
174        if self.name: rv['name'] = self.name
175        if self.version: rv['version'] = self.version
176        if self.distribution: rv['version'] = self.distribution
177        if self.distributionversion: rv['version'] = self.distributionversion
178        if self.attribute:
179            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
180        return rv
181
182class Software(base):
183    def __init__(self, location, install=None, attribute=[]):
184        self.location = location
185        self.install = install
186        self.attribute = [ self.init_class(Attribute, a)\
187                for a in self.make_list(attribute) ]
188
189    def clone(self):
190        return Software(location=self.location, install=self.install, 
191                attribute=[a.clone() for a in self.attribute])
192
193    def to_dict(self):
194        rv = { 'location': self.location }
195        if self.install: rv['install'] = self.install
196        if self.attribute:
197            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
198        return rv
199
200class Interface(base):
201    def __init__(self, substrate, capacity=None, latency=None, attribute=[],
202            element=None):
203        self.substrate = self.make_list(substrate)
204        self.capacity = self.init_class(Capacity, capacity)
205        self.latency = self.init_class(Latency, latency)
206        self.attribute = [ self.init_class(Attribute, a) \
207                for a in self.make_list(attribute) ]
208        self.element = element
209        self.subs = [ ]
210
211    def clone(self):
212        if self.capacity: c = self.capacity.clone()
213        else: c = None
214
215        if self.latency: l = self.latency.clone()
216        else: l = None
217
218        return Interface(substrate=self.substrate,
219                capacity=c, latency=l,
220                attribute = [ a.clone() for a in self.attribute])
221
222    def to_dict(self):
223        rv = { 'substrate': self.substrate }
224        if self.capacity:
225            rv['capacity'] = self.capacity.to_dict()
226        if self.latency:
227            rv['latency'] = self.latency.to_dict()
228        if self.attribute:
229            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
230        return rv
231
232class ID(base):
233    def __init__(self, fedid=None, uuid=None, uri=None, localname=None,
234            kerberosUsername=None):
235        self.fedid=fedid
236        self.uuid = uuid
237        self.uri = uri
238        self.localname = localname
239        self.kerberosUsername = kerberosUsername
240
241    def clone(self):
242        return ID(self.fedid, self.uuid, self.uri, self.localname,
243                self.kerberosUsername)
244
245    def to_dict(self):
246        rv = { }
247        if self.fedid: rv['fedid'] = self.fedid
248        if self.uuid: rv['uuid'] = self.uuid
249        if self.uri: rv['uri'] = self.uri
250        if self.localname: rv['localname'] = self.localname
251        if self.kerberosUsername: rv['kerberosUsername'] = self.kerberosUsername
252        return rv
253
254class Computer(base):
255    def __init__(self, name=[], cpu=[], os=[], software=[], storage=[],
256            interface=[], attribute=[]):
257        def assign_element(i):
258            i.element = self
259
260        self.name = self.make_list(name)
261        self.cpu = [ self.init_class(CPU, c)  for c in self.make_list(cpu) ]
262        self.os = [ self.init_class(OperatingSystem, c) \
263                for c in self.make_list(os) ]
264        self.software = [ self.init_class(Software, c) \
265                for c in self.make_list(software) ]
266        self.storage = [ self.init_class(Storage, c) \
267                for c in self.make_list(storage) ]
268        self.interface = [ self.init_class(Interface, c) \
269                for c in self.make_list(interface) ]
270        self.attribute = [ self.init_class(Attribute, a) \
271                for a in self.make_list(attribute) ]
272        map(assign_element, self.interface)
273
274    def clone(self):
275        return Computer(name=self.name,
276                cpu=[x.clone() for x in self.cpu],
277                os=[x.clone() for x in self.os],
278                software=[x.clone() for x in self.software],
279                storage=[x.clone() for x in self.storage],
280                interface=[x.clone() for x in self.interface],
281                attribute=[x.clone() for x in self.attribute])
282
283    def to_dict(self):
284        rv = { }
285        if self.name:
286            rv['name'] = self.name
287        if self.cpu:
288            rv['cpu'] = [ c.to_dict() for  c in self.cpu ]
289        if self.os:
290            rv['os'] = [ o.to_dict() for o in self.os ]
291        if self.software:
292            rv['software'] = [ s.to_dict() for s in self.software ]
293        if self.storage:
294            rv['storage'] = [ s.to_dict for s in self.storage ]
295        if self.interface:
296            rv['interface'] = [ i.to_dict() for i in self.interface ]
297        if self.attribute:
298            rv['attribute'] = [ i.to_dict() for i in self.attribute ]
299        return { 'computer': rv }
300
301
302class Testbed(base):
303    def __init__(self, uri, type, interface=[], attribute=[]):
304        self.uri = uri
305        self.type = type
306        self.interface = [ self.init_class(Interface, c) \
307                for c in self.make_list(interface) ]
308        self.attribute = [ self.init_class(Attribute, c) \
309                for c in self.make_list(attribute) ]
310
311    def clone(self):
312        return Testbed(self.uri, self.type,
313                interface=[i.clone() for i in self.interface],
314                attribute=[a.cone() for a in self.attribute])
315
316    def to_dict(self):
317        rv = { }
318        if self.uri: rv['uri'] = self.uri
319        if self.type: rv['type'] = self.type
320        if self.interface:
321            rv['interface'] = [ i.to_dict() for i in self.interface]
322        if self.attribute:
323            rv['attribute'] = [ a.to_dict() for a in self.attribute]
324        return { 'testbed': rv }
325
326class Segment(base):
327    def __init__(self, id, type, uri, interface=[], attribute=[]):
328        self.id = self.init_class(ID, id)
329        self.type = type
330        self.uri = uri
331        self.interface = [ self.init_class(Interface, c) \
332                for c in self.make_list(interface) ]
333        self.attribute = [ self.init_class(Attribute, c) \
334                for c in self.make_list(attribute) ]
335
336    def clone(self):
337        return Segment(self.id.clone(), self.type, self.uri, 
338                interface=[i.clone() for i in self.interface], 
339                attribute=[a.clone() for a in self.attribute])
340
341    def to_dict(self):
342        rv = { }
343        if self.id: rv['id'] = self.id.to_dict()
344        if self.type: rv['type'] = self.type
345        if self.uri: rv['uri'] = self.uri
346        if self.interface:
347            rv['interface'] = [ i.to_dict() for i in self.interface ]
348        if self.attribute:
349            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
350        return { 'segment': rv }
351
352
353class Other(base):
354    def __init__(self, interface=[], attribute=[]):
355        self.interface = [ self.init_class(Interface, c) \
356                for c in self.make_list(interface) ]
357        self.attribute = [ self.init_class(Attribute, c) \
358                for c in self.make_list(attribute) ]
359
360    def clone(self):
361        return Other(interface=[i.clone() for i in self.interface], 
362                attribute=[a.clone() for a in attribute])
363
364    def to_dict(self):
365        rv = {}
366        if self.interface:
367            rv['interface'] = [ i.to_dict() for i in self.interface ]
368        if self.attribute:
369            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
370        return {'other': rv }
371
372
373class Topology(base):
374    @staticmethod
375    def init_element(e):
376        """
377        e should be of the form { typename: args } where args is a dict full of
378        the right parameters to initialize the element.  e should have only one
379        key, but we walk e's keys in an arbitrary order and instantiate the
380        first key we know how to.
381        """
382        classmap = {
383                'computer': Computer,
384                'testbed': Testbed,
385                'segment': Segment,
386                'other': Other,
387            }
388
389        if isinstance(e, dict):
390            for k in e.keys():
391                cl = classmap.get(k, None)
392                if cl: return cl(**e[k])
393        else:
394            return e
395
396    def __init__(self, substrates=[], elements=[], attribute=[]):
397        self.substrates = [ self.init_class(Substrate, s) \
398                for s in self.make_list(substrates) ]
399        self.elements = [ self.init_element(e) \
400                for e in self.make_list(elements) ]
401        self.attribute = [ self.init_class(Attribute, c) \
402                for c in self.make_list(attribute) ]
403        self.incorporate_elements()
404
405    def incorporate_elements(self):
406
407        # Could to this init in one gulp, but we want to look for duplicate
408        # substrate names
409        substrate_map = { }
410        for s in self.substrates:
411            s.interfaces = [ ]
412            if not substrate_map.has_key(s.name):
413                substrate_map[s.name] = s
414            else:
415                raise ConsistencyError("Duplicate substrate name %s" % s.name)
416
417        for e in self.elements:
418            for i in e.interface:
419                i.element = e
420                i.subs = [ ]
421                for sn in i.substrate:
422                    # NB, interfaces have substrate names in their substrate
423                    # attribute.
424                    if substrate_map.has_key(sn):
425                        sub = substrate_map[sn]
426                        i.subs.append(sub)
427                        sub.interfaces.append(i)
428                    else:
429                        raise ConsistencyError("No such substrate for %s" % sn)
430
431    def clone(self):
432        return Topology(substrates=[s.clone() for s in self.substrates], 
433                elements=[e.clone() for e in self.elements],
434                attribute=[a.clone() for a in self.attribute])
435
436
437    def make_indices(self):
438        sub_index = dict([(s.name, s) for s in self.substrates])
439        elem_index = dict([(n, e) for e in self.elements for n in e.name])
440
441    def to_dict(self):
442        rv = { }
443        if self.substrates:
444            rv['substrates'] = [ s.to_dict() for s in self.substrates ]
445        if self.elements:
446            rv['elements'] = [ s.to_dict() for s in self.elements ]
447        if self.attribute:
448            rv['attribute'] = [ s.to_dict() for s in self.attribute]
449        return rv
450
451def topology_from_xml(string=None, file=None, filename=None, top="topology"):
452    import xml.parsers.expat
453
454    class parser:
455        def __init__(self):
456            self.stack = [ ]
457            self.chars = ""
458            self.key = ""
459            self.have_chars = False
460            self.current = { }
461            self.in_cdata = False
462       
463        def start_element(self, name, attrs):
464            self.chars = ""
465            self.have_chars = False
466            self.key = str(name)
467            self.stack.append((self.current, self.key))
468            self.current = { }
469
470        def end_element(self, name):
471            if self.have_chars:
472                self.chars = self.chars.strip()
473                if len(self.chars) >0:
474                    addit = self.chars
475                else:
476                    addit = self.current
477            else:
478                addit = self.current
479
480            parent, key = self.stack.pop()
481            if parent.has_key(key):
482                if isinstance(parent[key], list):
483                    parent[key].append(addit)
484                else:
485                    parent[key] = [parent[key], addit]
486            else:
487                parent[key] = addit
488            self.current = parent
489            self.key = key
490
491            self.chars = ""
492            self.have_chars = False
493
494        def char_data(self, data):
495            self.have_chars = True
496            self.chars += data
497
498    p = parser()
499    xp = xml.parsers.expat.ParserCreate()
500
501    xp.StartElementHandler = p.start_element
502    xp.EndElementHandler = p.end_element
503    xp.CharacterDataHandler = p.char_data
504
505    num_set = len([ x for x in (string, filename, file)\
506            if x is not None ])
507
508    if num_set != 1:
509        raise RuntimeError("Exactly one one of file, filename and string " + \
510                "must be set")
511    elif filename:
512        f = open(filename, "r")
513        xp.ParseFile(f)
514        f.close()
515    elif file:
516        xp.ParseFile(file)
517    elif string:
518        xp.Parse(string, isfinal=True)
519    else:
520        return None
521
522    return Topology(**p.current[top])
523
524def topology_to_xml(t, top=None):
525    """
526    Print the topology as XML.  This is quick and dirty, but should work for
527    many purposes.  Convert the topology to a dict and print it recursively.
528    """
529    from xml.sax.saxutils import escape
530
531    def dict_to_xml(e, top=None):
532        if top: rv = "<%s>" % top
533        else: rv = ""
534
535        for k in e.keys():
536            if isinstance(e[k], basestring):
537                rv += "<%s>%s</%s>" % (k, escape(e[k]), k)
538            elif isinstance(e[k], (int, float, long)):
539                rv += "<%s>%d</%s>" % (k, e[k], k)
540            elif isinstance(e[k], dict):
541                rv += "<%s>%s</%s>" % (k, dict_to_xml(e[k]), k)
542            elif getattr(e[k], '__iter__', None):
543                for ee in e[k]:
544                    if isinstance(ee, dict):
545                        rv += "<%s>%s</%s>" % (k, dict_to_xml(ee), k)
546                    else:
547                        rv += "<%s>%s</%s>" % (k, escape(ee), k)
548            else:
549                try:
550                    rv += "<%s>%s</%s>" % (k, e[k], k)
551                except Exception:
552                    raise ConsistencyError("What is this?? %s %s" % (k, e[k]))
553        if top: rv += "</%s>" % top
554        return rv
555
556    return dict_to_xml(t.to_dict(), top)
557
558
559def topology_to_vtopo(t):
560    nodes = [ ]
561    lans = [ ]
562
563    for eidx, e in enumerate(t.elements):
564        if e.name: name = e.name[0]
565        else: name = "unnamed_node%d" % eidx
566       
567        ips = [ ]
568        for idx, i in enumerate(e.interface):
569            ip = i.get_attribute('ip4_address')
570            ips.append(ip)
571            port = "%s:%d" % (name, idx)
572            for idx, s in enumerate(i.subs):
573                bw = 100000
574                delay = 0.0
575                if s.capacity:
576                    bw = s.capacity.rate
577                if i.capacity:
578                    bw = i.capacity.rate
579
580                if s.latency:
581                    delay = s.latency.time
582                if i.latency:
583                    bw = i.latency.time
584
585                lans.append({
586                    'member': port,
587                    'vname': s.name,
588                    'ip': ip,
589                    'vnode': name,
590                    'delay': delay,
591                    'bandwidth': bw,
592                    })
593        nodes.append({
594            'ips': ":".join(ips),
595            'vname': name,
596            })
597
598    return { 'node': nodes, 'lan': lans }
599
600def to_tcl_name(n):
601    t = re.sub('-(\d+)', '(\1)', n)
602    return t
603
604def generate_portal_command_filter(cmd, add_filter=None):
605    def rv(e):
606        s =""
607        if isinstance(e, Computer):
608            gw = e.get_attribute('portal')
609            if add_filter and callable(add_filter):
610                add = add_filter(e)
611            else:
612                add = True
613            if gw and add:
614                s = "%s $%s\n" % (cmd, to_tcl_name(e.name[0]))
615        return s
616    return rv
617
618def generate_portal_image_filter(image):
619    def rv(e):
620        s =""
621        if isinstance(e, Computer):
622            gw = e.get_attribute('portal')
623            if gw:
624                s = "tb-set-node-os $%s %s\n" % (to_tcl_name(e.name[0]), image)
625        return s
626    return rv
627
628def generate_portal_hardware_filter(type):
629    def rv(e):
630        s =""
631        if isinstance(e, Computer):
632            gw = e.get_attribute('portal')
633            if gw:
634                s = "tb-set-hardware $%s %s\n" % (to_tcl_name(e.name[0]), type)
635        return s
636    return rv
637
638
639def topology_to_ns2(t, filters=[], routing="Session"):
640    out = """
641set ns [new Simulator]
642source tb_compat.tcl
643
644"""
645
646    for e in t.elements:
647        rpms = ""
648        tarfiles = ""
649        if isinstance(e, Computer):
650            name = to_tcl_name(e.name[0])
651            out += "set %s [$ns node]\n" % name
652            if e.os and len(e.os) == 1:
653                osid = e.os[0].get_attribute('osid')
654                if osid:
655                    out += "tb-set-node-os $%s %s\n" % (name, osid)
656            hw = e.get_attribute('type')
657            if hw:
658                out += "tb-set-hardware $%s %s\n" % (name, hw)
659            for s in e.software:
660                if s.install:
661                    tarfiles += "%s %s " % (s.install, s.location)
662                else:
663                    rpms += "%s " % s.location
664            if rpms:
665                out += "tb-set-node-rpms $%s %s\n" % (name, rpms)
666            if tarfiles:
667                out += "tb-set-node-tarfiles $%s %s\n" % (name, tarfiles)
668            startcmd = e.get_attribute('startup')
669            if startcmd:
670                out+= 'tb-set-node-startcmd $%s "%s"\n' % (name, startcmd)
671            for f in filters:
672                out += f(e)
673            out+= "\n"
674   
675    for idx, s in enumerate(t.substrates):
676        loss = s.get_attribute('loss')
677        if s.latency:
678            delay = s.latency.time
679        else:
680            delay = 0
681        name = to_tcl_name(s.name or "sub%d" % idx)
682
683        if len(s.interfaces) > 2:
684            # Lan
685            members = [ to_tcl_name("$%s") % i.element.name[0] \
686                    for i in s.interfaces]
687            out += 'set %s [$ns make-lan "%s" %fkb %fms ]\n' % \
688                    (name, " ".join(members), s.capacity.rate, delay)
689            if loss:
690                "tb-set-lan-loss $%s %f\n" % (name, float(loss))
691
692            for i in s.interfaces:
693                e = i.element
694                ip = i.get_attribute("ip4_address")
695                if ip:
696                    out += "tb-set-ip-lan $%s $%s %s\n" % \
697                            (to_tcl_name(e.name[0]), name, ip)
698                if i.capacity and i.capacity.rate != s.capacity.rate:
699                    out += "tb-set-node-lan-bandwidth $%s $%s %fkb\n" % \
700                            (to_tcl_name(e.name[0]), name, i.capacity.rate)
701                if i.latency and i.latency.time != delay:
702                    out += "tb-set-node-lan-delay $%s $%s %fms\n" % \
703                            (to_tcl_name(e.name[0]), name, i.latency.time)
704                iloss = i.get_attribute('loss')
705                if loss and iloss != loss :
706                    out += "tb-set-node-lan-loss $%s $%s %f\n" % \
707                            (to_tcl_name(e.name[0]), name, float(loss))
708            out+= "\n"
709        elif len(s.interfaces) == 2:
710            f = s.interfaces[0]
711            t = s.interfaces[1]
712
713            out += "set %s [$ns duplex-link $%s $%s %fkb %fms DropTail]\n" %\
714                    (name, to_tcl_name(f.element.name[0]), 
715                            to_tcl_name(t.element.name[0]),
716                            s.capacity.rate, delay)
717            if loss:
718                out += "tb-set-link-loss $%s %f\n" % (name, float(loss))
719
720            for i in s.interfaces:
721                lloss = i.get_attribute("loss")
722                cap_override = i.capacity and \
723                        i.capacity.rate != s.capacity.rate
724                delay_override = i.latency and \
725                        i.latency.time != delay
726                loss_override = lloss and lloss != loss
727                if cap_override or delay_override or loss_override:
728                    if i.capacity: cap = i.capacity.rate
729                    else: cap = s.capacity.rate
730
731                    if i.latency: delay = i.latency.time
732
733                    if lloss: loss = lloss
734                    else: loss = loss or 0.0
735
736                    out += "tb-set-link-simplex-params $%s $%s %fms %fkb %f\n"\
737                            % (name, to_tcl_name(i.element.name[0]),
738                                    delay, cap, loss)
739                ip = i.get_attribute('ip4_address')
740                if ip:
741                    out += "tb-set-ip-link $%s $%s %s\n" % \
742                            (to_tcl_name(i.element.name[0]), name, ip)
743            out+= "\n"
744        for f in filters:
745            out+= f(s)
746    out+="$ns rtproto %s" % routing
747    out+="""
748$ns run
749"""
750    return out
Note: See TracBrowser for help on using the repository browser.