source: fedd/federation/topdl.py @ ecca6eb

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

checkpoint

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