source: fedd/deter/topdl.py @ e1ed2a7b

Last change on this file since e1ed2a7b was f1302d1, checked in by Ted Faber <faber@…>, 12 years ago

Do not try to create substrates with fewer than 2 nodes on an emulab. This got lost in c7a6a2050c9ed682b54392925964b05fff78e808

  • Property mode set to 100644
File size: 33.4 KB
Line 
1#!/usr/local/bin/python
2
3import re
4import xml.parsers.expat
5from xml.sax.saxutils import escape
6from base64 import b64encode
7from string import join
8
9from fedid import fedid as fedid_class
10
11class base:
12    @staticmethod
13    def init_class(c, arg):
14        if isinstance(arg, dict):
15            try:
16                return c(**arg)
17            except:
18                print "%s" % arg
19                raise
20        elif isinstance(arg, c):
21            return arg
22        else:
23            return None
24
25    @staticmethod
26    def make_list(a):
27        if isinstance(a, basestring) or isinstance(a, dict): return [ a ]
28        elif getattr(a, '__iter__', None): return a
29        else: return [ a ]
30
31    @staticmethod
32    def init_string(s):
33        """
34        Force a string coercion for everything but a None.
35        """
36        if s is not None: return "%s" % s
37        else: return None
38
39    def remove_attribute(self, key):
40        to_del = None
41        attrs = getattr(self, 'attribute', [])
42        for i, a in enumerate(attrs):
43            if a.attribute == key:
44                to_del = i
45                break
46       
47        if to_del: del attrs[i]
48
49    def get_attribute(self, key):
50        rv = None
51        attrs = getattr(self, 'attribute', None)
52        if attrs:
53            for a in attrs:
54                if a.attribute == key:
55                    rv = a.value
56                    break
57        return rv
58
59    def set_attribute(self, key, value):
60        attrs = getattr(self, 'attribute', None)
61        if attrs is None:
62            return
63        for a in attrs:
64            if a.attribute == key: 
65                a.value = value
66                break
67        else:
68            attrs.append(Attribute(key, value))
69
70class ConsistencyError(RuntimeError): pass
71class NamespaceError(RuntimeError): pass
72
73class Attribute(base):
74    def __init__(self, attribute, value):
75        self.attribute = self.init_string(attribute)
76        self.value = self.init_string(value)
77
78    def clone(self):
79        return Attribute(attribute=self.attribute, value=self.value)
80
81    def to_dict(self):
82        return { 'attribute': self.attribute, 'value': self.value }
83    def to_xml(self):
84        return "<attribute>%s</attribute><value>%s</value>" % \
85                (escape(self.attribute), escape(self.value))
86
87class Capacity(base):
88    def __init__(self, rate, kind):
89        self.rate = float(rate)
90        self.kind = self.init_string(kind)
91
92    def clone(self):
93        return Capacity(rate=self.rate, kind=self.kind)
94
95    def to_dict(self):
96        return { 'rate': float(self.rate), 'kind': self.kind }
97
98    def to_xml(self):
99        return "<rate>%f</rate><kind>%s</kind>" % (self.rate, self.kind)
100
101class Latency(base):
102    def __init__(self, time, kind):
103        self.time = float(time)
104        self.kind = self.init_string(kind)
105
106    def clone(self):
107        return Latency(time=self.time, kind=self.kind)
108
109    def to_dict(self):
110        return { 'time': float(self.time), 'kind': self.kind }
111
112    def to_xml(self):
113        return "<time>%f</time><kind>%s</kind>" % (self.time, self.kind)
114
115class ServiceParam(base):
116    def __init__(self, name, type):
117        self.name = self.init_string(name)
118        self.type = self.init_string(type)
119
120    def clone(self):
121        return ServiceParam(self.name. self.type)
122
123    def to_dict(self):
124        return { 'name': name, 'type': type }
125
126    def to_xml(self):
127        return "<name>%s</name><type>%s</type>" % (self.name, self.type)
128
129class Service(base):
130    def __init__(self, name, importer=[], param=[], description=None, 
131            status=None):
132        self.name = self.init_string(name)
133        self.importer = [self.init_string(i) \
134                for i in self.make_list(importer)]
135        self.param = [ self.init_class(ServiceParam, p) \
136                for p in self.make_list(param) ]
137        self.description = self.init_string(description)
138        self.status = self.init_string(status)
139
140    def clone(self):
141        return Service(
142                name=self.name, 
143                importer=[ i for i in self.importer], 
144                param=[p.clone() for p in self.param], 
145                description=self.description,
146                status=self.status)
147
148    def to_dict(self):
149        rv = { }
150        if self.name is not None:
151            rv['name'] = self.name
152        if self.importer:
153            rv['importer'] = [ i for i in self.importer ]
154        if self.param:
155            rv['param'] = [ p.to_dict() for p in self.param ]
156        if self.description is not None:
157            rv['description'] = self.description
158        if self.status is not None:
159            rv['status'] = self.status
160        return rv
161
162    def to_xml(self):
163        rv = '' 
164        if self.name is not None:
165            rv += '<name>%s</name>' % self.name
166        if self.importer:
167            rv += join(['<importer>%s</importer>' % i \
168                    for i in self.importer],'')
169        if self.param:
170            rv += join(['<param>%s</param>' % p.to_xml() \
171                    for p in self.param], '')
172        if self.description is not None:
173            rv += '<description>%s</description>' % self.description
174        if self.status is not None:
175            rv += '<status>%s</status>' % self.status
176        return rv
177
178class Substrate(base):
179    def __init__(self, name, capacity=None, latency=None, attribute=[],
180            localname=[], status=None, service=[], operation=[]):
181        self.name = self.init_string(name)
182        self.capacity = self.init_class(Capacity, capacity)
183        self.latency = self.init_class(Latency, latency)
184        self.attribute = [ self.init_class(Attribute, a) \
185                for a in self.make_list(attribute) ]
186        self.localname = [ self.init_string(ln)\
187                for ln in self.make_list(localname) ]
188        self.status = self.init_string(status)
189        self.service = [ self.init_class(Service, s) \
190                for s in self.make_list(service)]
191        self.operation = [self.init_string(op) \
192                for op in self.make_list(operation)]
193        self.interfaces = [ ]
194
195    def clone(self):
196        if self.capacity: c = self.capacity.clone()
197        else: c = None
198
199        if self.latency: l = self.latency.clone()
200        else: l = None
201
202        return Substrate(name=self.name,
203                capacity=c,
204                latency=l,
205                attribute = [a.clone() for a in self.attribute],
206                localname = [ ln for ln in self.localname],
207                status = self.status,
208                service = [ s.clone() for s in self.service],
209                operation=[ op for op in self.operation])
210
211    def to_dict(self):
212        rv = { 'name': self.name }
213        if self.capacity:
214            rv['capacity'] = self.capacity.to_dict()
215        if self.latency:
216            rv['latency'] = self.latency.to_dict()
217        if self.attribute:
218            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
219        if self.localname:
220            rv['localname'] = [ ln for ln in self.localname ]
221        if self.status:
222            rv['status'] = self.status
223        if self.service:
224            rv['service'] = [s.to_dict() for s in self.service]
225        if self.operation:
226            rv['operation'] = [op for op in self.operation]
227        return rv
228
229    def to_xml(self):
230        rv = "<name>%s</name>" % escape(self.name)
231        if self.capacity is not None:
232            rv += "<capacity>%s</capacity>" % self.capacity.to_xml()
233        if self.latency is not None:
234            rv += "<latency>%s</latency>" % self.latency.to_xml()
235       
236        if self.attribute:
237            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
238                    for a in self.attribute], "")
239        if self.localname:
240            rv += join(['<localname>%s</localname>' % ln \
241                    for ln in self.localname], '')
242        if self.status is not None:
243            rv += '<status>%s</status>' % self.status
244        if self.service:
245            rv += join(['<service>%s</service' % s.to_xml() \
246                    for s in self.service], '')
247        if self.operation:
248            rv += join(['<operation>%s</operation>' % op \
249                    for op in self.operation], '')
250        return rv
251
252    def __getstate__(self):
253        d = self.__dict__.copy()
254        del d['interfaces']
255        return d
256
257    def __setstate__(self, d):
258        # Import everything from the pickle dict (except what we excluded in
259        # __getstate__)
260        self.__dict__.update(d)
261        self.interfaces = []
262
263class CPU(base):
264    def __init__(self, type, attribute=[]):
265        self.type = self.init_string(type)
266        self.attribute = [ self.init_class(Attribute, a) for a in \
267                self.make_list(attribute) ]
268
269    def clone(self):
270        return CPU(type=self.type,
271                attribute = [a.clone() for a in self.attribute])
272
273    def to_dict(self):
274        rv = { 'type': self.type}
275        if self.attribute:
276            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
277        return rv
278
279    def to_xml(self):
280        rv = "<type>%s</type>" % escape(self.type)
281        if self.attribute:
282            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
283                    for a in self.attribute], "")
284        return rv
285
286
287class Storage(base):
288    def __init__(self, amount, persistence, attribute=[]):
289        self.amount = float(amount)
290        self.persistence = self.init_string(persistence)
291        self.attribute = [ self.init_class(Attribute, a) \
292                for a in self.make_list(attribute) ]
293
294    def clone(self):
295        return Storage(amount=self.amount, persistence=self.persistence, 
296                attribute = [a.clone() for a in self.attribute])
297
298    def to_dict(self):
299        rv = { 'amount': float(self.amount), 'persistence': self.persistence }
300        if self.attribute:
301            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
302        return rv
303
304    def to_xml(self):
305        rv = "<amount>%f</amount><persistence>%s</persistence>" % \
306                (self.amount, escape(self.persistence))
307        if self.attribute:
308            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
309                    for a in self.attribute], "")
310        return rv
311
312
313class OperatingSystem(base):
314    def __init__(self, name=None, version=None, distribution=None,
315            distributionversion=None, attribute=[]):
316        self.name = self.init_string(name)
317        self.version = self.init_string(version)
318        self.distribution = self.init_string(distribution)
319        self.distributionversion = self.init_string(distributionversion)
320        self.attribute = [ self.init_class(Attribute, a) \
321                for a in self.make_list(attribute) ]
322
323    def clone(self):
324        return OperatingSystem(name=self.name,
325                version=self.version,
326                distribution=self.distribution,
327                distributionversion=self.distributionversion,
328                attribute = [ a.clone() for a in self.attribute])
329
330    def to_dict(self):
331        rv = { }
332        if self.name: rv['name'] = self.name
333        if self.version: rv['version'] = self.version
334        if self.distribution: rv['distribution'] = self.distribution
335        if self.distributionversion: 
336            rv['distributionversion'] = self.distributionversion
337        if self.attribute:
338            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
339        return rv
340
341    def to_xml(self):
342        rv = ""
343        if self.name: rv += "<name>%s</name>" % escape(self.name)
344        if self.version: rv += "<version>%s</version>" % escape(self.version)
345        if self.distribution: 
346            rv += "<distribution>%s</distribution>" % escape(self.distribution)
347        if self.distributionversion: 
348            rv += "<distributionversion>%s</distributionversion>" % \
349                    escape(self.distributionversion)
350       
351        if self.attribute:
352            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
353                    for a in self.attribute], "")
354        return rv
355
356
357class Software(base):
358    def __init__(self, location, install=None, attribute=[]):
359        self.location = self.init_string(location)
360        self.install = self.init_string(install)
361        self.attribute = [ self.init_class(Attribute, a)\
362                for a in self.make_list(attribute) ]
363
364    def clone(self):
365        return Software(location=self.location, install=self.install, 
366                attribute=[a.clone() for a in self.attribute])
367
368    def to_dict(self):
369        rv = { 'location': self.location }
370        if self.install: rv['install'] = self.install
371        if self.attribute:
372            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
373        return rv
374
375    def to_xml(self):
376        rv = "<location>%s</location>" % escape(self.location)
377        if self.install: rv += "<install>%s</install>" % self.install
378        if self.attribute:
379            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
380                    for a in self.attribute], "")
381        return rv
382
383
384class Interface(base):
385    def __init__(self, substrate, name=None, capacity=None, latency=None,
386            attribute=[], element=None):
387        self.name = self.init_string(name)
388
389        self.substrate = self.make_list(substrate)
390        self.capacity = self.init_class(Capacity, capacity)
391        self.latency = self.init_class(Latency, latency)
392        self.attribute = [ self.init_class(Attribute, a) \
393                for a in self.make_list(attribute) ]
394        self.element = element
395        self.subs = [ ]
396
397    def clone(self):
398        if self.capacity: c = self.capacity.clone()
399        else: c = None
400
401        if self.latency: l = self.latency.clone()
402        else: l = None
403
404        return Interface(substrate=[s for s in self.substrate], name=self.name,
405                capacity=c, latency=l,
406                attribute = [ a.clone() for a in self.attribute])
407
408    def to_dict(self):
409        rv = { 'substrate': self.substrate, 'name': self.name }
410        if self.capacity:
411            rv['capacity'] = self.capacity.to_dict()
412        if self.latency:
413            rv['latency'] = self.latency.to_dict()
414        if self.attribute:
415            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
416        return rv
417
418    def to_xml(self):
419        rv = join(["<substrate>%s</substrate>" % escape(s) \
420                for s in self.substrate], "")
421        rv += "<name>%s</name>" % self.name
422        if self.capacity:
423            rv += "<capacity>%s</capacity>" % self.capacity.to_xml()
424        if self.latency:
425            rv += "<latency>%s</latency>" % self.latency.to_xml()
426        if self.attribute:
427            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
428                    for a in self.attribute], "")
429        return rv
430
431    def __getstate__(self):
432        d = self.__dict__.copy()
433        del d['subs']
434        return d
435
436    def __setstate__(self, d):
437        # Import everything from the pickle dict (except what we excluded in
438        # __getstate__)
439        self.__dict__.update(d)
440        self.subs = []
441
442
443class ID(base):
444    def __init__(self, fedid=None, uuid=None, uri=None, localname=None,
445            kerberosUsername=None):
446        self.fedid=fedid_class(hexstr="%s" % fedid)
447        self.uuid = self.init_string(uuid)
448        self.uri = self.init_string(uri)
449        self.localname =self.init_string( localname)
450        self.kerberosUsername = self.init_string(kerberosUsername)
451
452    def clone(self):
453        return ID(self.fedid, self.uuid, self.uri, self.localname,
454                self.kerberosUsername)
455
456    def to_dict(self):
457        rv = { }
458        if self.fedid: rv['fedid'] = self.fedid
459        if self.uuid: rv['uuid'] = self.uuid
460        if self.uri: rv['uri'] = self.uri
461        if self.localname: rv['localname'] = self.localname
462        if self.kerberosUsername: rv['kerberosUsername'] = self.kerberosUsername
463        return rv
464
465    def to_xml(self):
466        if self.uuid: rv = "<uuid>%s</uuid>" % b64encode(self.uuid)
467        elif self.fedid: rv = "<fedid>%s</fedid>" % \
468                b64encode(self.fedid.get_bits())
469        elif self.uri: rv = "<uri>%s</uri>" % escape(self.uri)
470        elif self.localname: 
471            rv = "<localname>%s</localname>" % escape(self.localname)
472        elif self.kerberosUsername: 
473            rv = "<kerberosUsername>%s</kerberosUsername>" % \
474                    escape(self.kerberosUsername)
475        return rv
476
477class Computer(base):
478    def __init__(self, name, cpu=[], os=[], software=[], storage=[],
479            interface=[], attribute=[], localname=[], status=None, service=[],
480            operation=[]):
481        def assign_element(i):
482            i.element = self
483
484        self.name = self.init_string(name)
485        self.cpu = [ self.init_class(CPU, c)  for c in self.make_list(cpu) ]
486        self.os = [ self.init_class(OperatingSystem, c) \
487                for c in self.make_list(os) ]
488        self.software = [ self.init_class(Software, c) \
489                for c in self.make_list(software) ]
490        self.storage = [ self.init_class(Storage, c) \
491                for c in self.make_list(storage) ]
492        self.interface = [ self.init_class(Interface, c) \
493                for c in self.make_list(interface) ]
494        self.attribute = [ self.init_class(Attribute, a) \
495                for a in self.make_list(attribute) ]
496        self.localname = [ self.init_string(ln)\
497                for ln in self.make_list(localname) ]
498        self.status = self.init_string(status)
499        self.service = [ self.init_class(Service, s) \
500                for s in self.make_list(service)]
501        self.operation = [self.init_string(op) \
502                for op in self.make_list(operation)]
503        map(assign_element, self.interface)
504
505    def clone(self):
506        # Copy the list of names
507        return Computer(name=self.name,
508                cpu=[x.clone() for x in self.cpu],
509                os=[x.clone() for x in self.os],
510                software=[x.clone() for x in self.software],
511                storage=[x.clone() for x in self.storage],
512                interface=[x.clone() for x in self.interface],
513                attribute=[x.clone() for x in self.attribute],
514                localname =[ ln for ln in self.localname],
515                status = self.status,
516                service = [s.clone() for s in self.service],
517                operation = [op for op in self.operation])
518
519    def to_dict(self):
520        rv = { }
521        if self.name:
522            rv['name'] = self.name
523        if self.cpu:
524            rv['cpu'] = [ c.to_dict() for  c in self.cpu ]
525        if self.os:
526            rv['os'] = [ o.to_dict() for o in self.os ]
527        if self.software:
528            rv['software'] = [ s.to_dict() for s in self.software ]
529        if self.storage:
530            rv['storage'] = [ s.to_dict() for s in self.storage ]
531        if self.interface:
532            rv['interface'] = [ i.to_dict() for i in self.interface ]
533        if self.attribute:
534            rv['attribute'] = [ i.to_dict() for i in self.attribute ]
535        if self.localname:
536            rv['localname'] = [ ln for ln in self.localname ]
537        if self.status:
538            rv['status'] = self.status
539        if self.service:
540            rv['service'] = [s.to_dict() for s in self.service]
541        if self.operation:
542            rv['operation'] = [op for op in self.operation]
543        return { 'computer': rv }
544
545    def to_xml(self):
546        rv = "<name>%s</name>" % escape(self.name)
547        if self.cpu:
548            rv += join(["<cpu>%s</cpu>" % c.to_xml() for c in self.cpu], "")
549        if self.os:
550            rv += join(["<os>%s</os>" % o.to_xml() for o in self.os], "")
551        if self.software:
552            rv += join(["<software>%s</software>" % s.to_xml() \
553                    for s in self.software], "")
554        if self.storage:
555            rv += join(["<storage>%s</storage>" % s.to_xml() \
556                    for s in self.storage], "")
557        if self.interface:
558            rv += join(["<interface>%s</interface>" % i.to_xml() 
559                for i in self.interface], "")
560        if self.attribute:
561            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
562                    for a in self.attribute], "")
563        if self.localname:
564            rv += join(['<localname>%s</localname>' % ln \
565                    for ln in self.localname], '')
566        if self.status is not None:
567            rv += '<status>%s</status>' % self.status
568        if self.service:
569            rv += join(['<service>%s</service' % s.to_xml() \
570                    for s in self.service], '')
571        if self.operation:
572            rv += join(['<operation>%s</operation>' % op \
573                    for op in self.operation], '')
574        return "<computer>%s</computer>" % rv
575
576
577
578class Testbed(base):
579    def __init__(self, uri, type, interface=[], attribute=[], localname=[],
580            status=None, service=[], operation=[]):
581        self.uri = self.init_string(uri)
582        self.type = self.init_string(type)
583        self.interface = [ self.init_class(Interface, c) \
584                for c in self.make_list(interface) ]
585        self.attribute = [ self.init_class(Attribute, c) \
586                for c in self.make_list(attribute) ]
587        self.localname = [ self.init_string(ln)\
588                for ln in self.make_list(localname) ]
589        self.status = self.init_string(status)
590        self.service = [ self.init_class(Service, s) \
591                for s in self.make_list(service)]
592        self.operation = [self.init_string(op) \
593                for op in self.make_list(operation)]
594
595    def clone(self):
596        return Testbed(self.uri, self.type,
597                interface=[i.clone() for i in self.interface],
598                attribute=[a.clone() for a in self.attribute],
599                localname = [ ln for ln in self.localname ],
600                status=self.status,
601                service=[s.clone() for s in self.service ],
602                operation = [ op for op in self.operation ])
603
604    def to_dict(self):
605        rv = { }
606        if self.uri: rv['uri'] = self.uri
607        if self.type: rv['type'] = self.type
608        if self.interface:
609            rv['interface'] = [ i.to_dict() for i in self.interface]
610        if self.attribute:
611            rv['attribute'] = [ a.to_dict() for a in self.attribute]
612        if self.localname:
613            rv['localname'] = [ ln for ln in self.localname ]
614        if self.status:
615            rv['status'] = self.status
616        if self.service:
617            rv['service'] = [s.to_dict() for s in self.service]
618        if self.operation:
619            rv['operation'] = [op for op in self.operation]
620        return { 'testbed': rv }
621
622    def to_xml(self):
623        rv = "<uri>%s</uri><type>%s</type>" % \
624                (escape(self.uri), escape(self.type))
625        if self.interface:
626            rv += join(["<interface>%s</interface>" % i.to_xml() 
627                for i in self.interface], "")
628        if self.attribute:
629            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
630                    for a in self.attribute], "")
631        if self.localname:
632            rv += join(['<localname>%s</localname>' % ln \
633                    for ln in self.localname], '')
634        if self.status is not None:
635            rv += '<status>%s</status>' % self.status
636        if self.service:
637            rv += join(['<service>%s</service' % s.to_xml() \
638                    for s in self.service], '')
639        if self.operation:
640            rv += join(['<operation>%s</operation>' % op \
641                    for op in self.operation], '')
642        return "<testbed>%s</testbed>" % rv
643
644       
645
646class Segment(base):
647    def __init__(self, id, type, uri, interface=[], attribute=[]):
648        self.id = self.init_class(ID, id)
649        self.type = self.init_string(type)
650        self.uri = self.init_string(uri)
651        self.interface = [ self.init_class(Interface, c) \
652                for c in self.make_list(interface) ]
653        self.attribute = [ self.init_class(Attribute, c) \
654                for c in self.make_list(attribute) ]
655
656    def clone(self):
657        return Segment(self.id.clone(), self.type, self.uri, 
658                interface=[i.clone() for i in self.interface], 
659                attribute=[a.clone() for a in self.attribute])
660
661    def to_dict(self):
662        rv = { }
663        if self.id: rv['id'] = self.id.to_dict()
664        if self.type: rv['type'] = self.type
665        if self.uri: rv['uri'] = self.uri
666        if self.interface:
667            rv['interface'] = [ i.to_dict() for i in self.interface ]
668        if self.attribute:
669            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
670        return { 'segment': rv }
671
672    def to_xml(self):
673        rv = "<id>%s</id><uri>%s</uri><type>%s</type>" % \
674                (self.id.to_xml(), escape(self.uri), escape(self.type))
675        if self.interface:
676            rv += join(["<interface>%s</interface>" % i.to_xml() 
677                for i in self.interface], "")
678        if self.attribute:
679            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
680                    for a in self.attribute], "")
681        return "<segment>%s</segment>" % rv
682
683class Other(base):
684    def __init__(self, interface=[], attribute=[]):
685        self.interface = [ self.init_class(Interface, c) \
686                for c in self.make_list(interface) ]
687        self.attribute = [ self.init_class(Attribute, c) \
688                for c in self.make_list(attribute) ]
689
690    def clone(self):
691        return Other(interface=[i.clone() for i in self.interface], 
692                attribute=[a.clone() for a in attribute])
693
694    def to_dict(self):
695        rv = {}
696        if self.interface:
697            rv['interface'] = [ i.to_dict() for i in self.interface ]
698        if self.attribute:
699            rv['attribute'] = [ a.to_dict() for a in self.attribute ]
700        return {'other': rv }
701
702    def to_xml(self):
703        rv = ""
704        if self.interface:
705            rv += join(["<interface>%s</interface>" % i.to_xml() 
706                for i in self.interface], "")
707        if self.attribute:
708            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
709                    for a in self.attribute], "")
710        return "<other>%s</other>" % rv
711
712class Topology(base):
713    version = "1.0"
714    @staticmethod
715    def init_element(e):
716        """
717        e should be of the form { typename: args } where args is a dict full of
718        the right parameters to initialize the element.  e should have only one
719        key, but we walk e's keys in an arbitrary order and instantiate the
720        first key we know how to.
721        """
722        classmap = {
723                'computer': Computer,
724                'testbed': Testbed,
725                'segment': Segment,
726                'other': Other,
727            }
728
729        if isinstance(e, dict):
730            for k in e.keys():
731                cl = classmap.get(k, None)
732                if cl: return cl(**e[k])
733        else:
734            return e
735
736    def __init__(self, substrates=[], elements=[], attribute=[], 
737            version=None):
738
739        if version is None: self.version = Topology.version
740        else: self.version = version
741
742        self.substrates = [ self.init_class(Substrate, s) \
743                for s in self.make_list(substrates) ]
744        self.elements = [ self.init_element(e) \
745                for e in self.make_list(elements) ]
746        self.attribute = [ self.init_class(Attribute, c) \
747                for c in self.make_list(attribute) ]
748        self.incorporate_elements()
749
750    @staticmethod
751    def name_element_interfaces(e):
752        names = set([i.name for i in e.interface if i.name])
753        inum = 0
754        for i in [ i for i in e.interface if not i.name]:
755            while inum < 1000:
756                n = "inf%03d" % inum
757                inum += 1
758                if n not in names:
759                    i.name = n
760                    break
761            else:
762                raise NamespaceError("Cannot make new interface name")
763
764
765
766    def name_interfaces(self):
767        """
768        For any interface without a name attribute, assign a unique one within
769        its element.
770        """
771
772        for e in self.elements:
773            self.name_element_interfaces(e)
774
775
776    def incorporate_elements(self):
777
778        # Could to this init in one gulp, but we want to look for duplicate
779        # substrate names
780        substrate_map = { }
781        for s in self.substrates:
782            s.interfaces = [ ]
783            if not substrate_map.has_key(s.name):
784                substrate_map[s.name] = s
785            else:
786                raise ConsistencyError("Duplicate substrate name %s" % s.name)
787
788        for e in self.elements:
789            self.name_element_interfaces(e)
790            for i in e.interface:
791                i.element = e
792                i.subs = [ ]
793                for sn in i.substrate:
794                    # NB, interfaces have substrate names in their substrate
795                    # attribute.
796                    if substrate_map.has_key(sn):
797                        sub = substrate_map[sn]
798                        i.subs.append(sub)
799                        sub.interfaces.append(i)
800                    else:
801                        raise ConsistencyError("No such substrate for %s" % sn)
802
803    def clone(self):
804        return Topology(substrates=[s.clone() for s in self.substrates], 
805                elements=[e.clone() for e in self.elements],
806                attribute=[a.clone() for a in self.attribute],
807                version=self.version)
808
809
810    def make_indices(self):
811        sub_index = dict([(s.name, s) for s in self.substrates])
812        elem_index = dict([(n, e) for e in self.elements for n in e.name])
813
814    def to_dict(self):
815        rv = { }
816        rv['version'] = self.version
817        if self.substrates:
818            rv['substrates'] = [ s.to_dict() for s in self.substrates ]
819        if self.elements:
820            rv['elements'] = [ s.to_dict() for s in self.elements ]
821        if self.attribute:
822            rv['attribute'] = [ s.to_dict() for s in self.attribute]
823        return rv
824
825    def to_xml(self):
826        rv = "<version>%s</version>" % escape(self.version)
827        if self.substrates:
828            rv += join(["<substrates>%s</substrates>" % s.to_xml() \
829                    for s in self.substrates], "")
830        if self.elements:
831            rv += join(["<elements>%s</elements>" % e.to_xml() \
832                    for e in self.elements], "")
833        if self.attribute:
834            rv += join(["<attribute>%s</attribute>" % a.to_xml() \
835                    for a in self.attribute], "")
836        return rv
837
838    def __setstate__(self, d):
839        # Import everything from the pickle dict and call incorporate to
840        # connect them.
841        self.__dict__.update(d)
842        self.incorporate_elements()
843
844
845def topology_from_xml(string=None, file=None, filename=None, top="topology"):
846    class parser:
847        def __init__(self, top):
848            self.stack = [ ]
849            self.chars = ""
850            self.key = ""
851            self.have_chars = False
852            self.current = { }
853            self.in_cdata = False
854            self.in_top = False
855            self.top = top
856       
857        def start_element(self, name, attrs):
858            self.chars = ""
859            self.have_chars = False
860            self.key = str(name)
861
862            if name == self.top:
863                self.in_top = True
864
865            if self.in_top:
866                self.stack.append((self.current, self.key))
867                self.current = { }
868
869        def end_element(self, name):
870            if self.in_top:
871                if self.have_chars:
872                    self.chars = self.chars.strip()
873                    if len(self.chars) >0:
874                        addit = self.chars
875                    else:
876                        addit = self.current
877                else:
878                    addit = self.current
879
880                parent, key = self.stack.pop()
881                if parent.has_key(key):
882                    if isinstance(parent[key], list):
883                        parent[key].append(addit)
884                    else:
885                        parent[key] = [parent[key], addit]
886                else:
887                    parent[key] = addit
888                self.current = parent
889                self.key = key
890
891            self.chars = ""
892            self.have_chars = False
893
894            if name == self.top:
895                self.in_top= False
896
897        def char_data(self, data):
898            if self.in_top:
899                self.have_chars = True
900                self.chars += data
901
902    p = parser(top=top)
903    xp = xml.parsers.expat.ParserCreate()
904
905    xp.StartElementHandler = p.start_element
906    xp.EndElementHandler = p.end_element
907    xp.CharacterDataHandler = p.char_data
908
909    num_set = len([ x for x in (string, filename, file)\
910            if x is not None ])
911
912    if num_set != 1:
913        raise RuntimeError("Exactly one one of file, filename and string " + \
914                "must be set")
915    elif filename:
916        f = open(filename, "r")
917        xp.ParseFile(f)
918        f.close()
919    elif file:
920        xp.ParseFile(file)
921    elif string:
922        xp.Parse(string, True)
923    else:
924        return None
925
926    return Topology(**p.current[top])
927
928def topology_from_startsegment(req):
929    """
930    Generate a topology from a StartSegment request to an access controller.
931    This is a little helper to avoid some gross looking syntax.  It accepts
932    either a request enclosed in the StartSegmentRequestBody, or one with that
933    outer dict removed.
934    """
935
936    if 'StartSegmentRequestBody' in req: r = req['StartSegmentRequestBody']
937    else: r = req
938   
939    if 'segmentdescription' in r and \
940            'topdldescription' in r['segmentdescription']:
941        return Topology(**r['segmentdescription']['topdldescription'])
942    else:
943        return None
944
945def topology_to_xml(t, top=None):
946    """
947    Print the topology as XML, recursively using the internal classes to_xml()
948    methods.
949    """
950
951    if top: return "<%s>%s</%s>" % (top, t.to_xml(), top)
952    else: return t.to_xml()
953
954def topology_to_vtopo(t):
955    nodes = [ ]
956    lans = [ ]
957
958    for eidx, e in enumerate(t.elements):
959        if isinstance(e, Computer):
960            if e.name: name = e.name
961            else: name = "unnamed_node%d" % eidx
962           
963            ips = [ ]
964            for idx, i in enumerate(e.interface):
965                ip = i.get_attribute('ip4_address')
966                ips.append(ip)
967                port = "%s:%d" % (name, idx)
968                for idx, s in enumerate(i.subs):
969                    bw = 100000
970                    delay = 0.0
971                    if s.capacity:
972                        bw = s.capacity.rate
973                    if i.capacity:
974                        bw = i.capacity.rate
975
976                    if s.latency:
977                        delay = s.latency.time
978                    if i.latency:
979                        bw = i.latency.time
980
981                    lans.append({
982                        'member': port,
983                        'vname': s.name,
984                        'ip': ip,
985                        'vnode': name,
986                        'delay': delay,
987                        'bandwidth': bw,
988                        })
989            nodes.append({
990                'ips': ":".join(ips),
991                'vname': name,
992                })
993
994    return { 'node': nodes, 'lan': lans }
995
996def to_tcl_name(n):
997    t = re.sub('-(\d+)', '(\\1)', n)
998    return t
999
1000def generate_portal_command_filter(cmd, add_filter=None):
1001    def rv(e):
1002        s =""
1003        if isinstance(e, Computer):
1004            gw = e.get_attribute('portal')
1005            if add_filter and callable(add_filter):
1006                add = add_filter(e)
1007            else:
1008                add = True
1009            if gw and add:
1010                s = "%s ${%s}\n" % (cmd, to_tcl_name(e.name))
1011        return s
1012    return rv
1013
1014def generate_portal_image_filter(image):
1015    def rv(e):
1016        s =""
1017        if isinstance(e, Computer):
1018            gw = e.get_attribute('portal')
1019            if gw:
1020                s = "tb-set-node-os ${%s} %s\n" % (to_tcl_name(e.name), image)
1021        return s
1022    return rv
1023
1024def generate_portal_hardware_filter(type):
1025    def rv(e):
1026        s =""
1027        if isinstance(e, Computer):
1028            gw = e.get_attribute('portal')
1029            if gw:
1030                s = "tb-set-hardware ${%s} %s\n" % (to_tcl_name(e.name), type)
1031        return s
1032    return rv
1033
1034
1035def topology_to_ns2(t, filters=[], routing="Manual"):
1036    out = """
1037set ns [new Simulator]
1038source tb_compat.tcl
1039
1040"""
1041
1042    for e in t.elements:
1043        rpms = ""
1044        tarfiles = ""
1045        if isinstance(e, Computer):
1046            name = to_tcl_name(e.name)
1047            out += "set %s [$ns node]\n" % name
1048            if e.os and len(e.os) == 1:
1049                osid = e.os[0].get_attribute('osid')
1050                if osid:
1051                    out += "tb-set-node-os ${%s} %s\n" % (name, osid)
1052            hw = e.get_attribute('type')
1053            if hw:
1054                out += "tb-set-hardware ${%s} %s\n" % (name, hw)
1055            for s in e.software:
1056                if s.install:
1057                    tarfiles += "%s %s " % (s.install, s.location)
1058                else:
1059                    rpms += "%s " % s.location
1060            if rpms:
1061                out += "tb-set-node-rpms ${%s} %s\n" % (name, rpms)
1062            if tarfiles:
1063                out += "tb-set-node-tarfiles ${%s} %s\n" % (name, tarfiles)
1064            startcmd = e.get_attribute('startup')
1065            if startcmd:
1066                out+= 'tb-set-node-startcmd ${%s} "%s"\n' % (name, startcmd)
1067            for f in filters:
1068                out += f(e)
1069            out+= "\n"
1070   
1071    for idx, s in enumerate(t.substrates):
1072        if len(s.interfaces) < 2: continue
1073        loss = s.get_attribute('loss')
1074        if s.latency: delay = s.latency.time
1075        else: delay = 0
1076
1077        if s.capacity: rate = s.capacity.rate
1078        else: rate = 100000
1079        name = to_tcl_name(s.name or "sub%d" % idx)
1080
1081        # Lan
1082        members = [ to_tcl_name("${%s}") % i.element.name \
1083                for i in s.interfaces]
1084        out += 'set %s [$ns make-lan "%s" %fkb %fms ]\n' % \
1085                (name, " ".join([to_tcl_name(m) for m in members]),
1086                        rate, 2 * delay)
1087        if loss:
1088            "tb-set-lan-loss ${%s} %f\n" % (name, float(loss))
1089
1090        for i in s.interfaces:
1091            e = i.element
1092            ip = i.get_attribute("ip4_address")
1093            if ip:
1094                out += "tb-set-ip-lan ${%s} ${%s} %s\n" % \
1095                        (to_tcl_name(e.name), name, ip)
1096            if i.capacity :
1097                out += "tb-set-node-lan-bandwidth ${%s} ${%s} %fkb\n" % \
1098                        (to_tcl_name(e.name), name, i.capacity.rate)
1099            if i.latency :
1100                out += "tb-set-node-lan-delay ${%s} ${%s} %fms\n" % \
1101                        (to_tcl_name(e.name), name, i.latency.time)
1102            iloss = i.get_attribute('loss')
1103            if loss and iloss != loss :
1104                out += "tb-set-node-lan-loss ${%s} ${%s} %f\n" % \
1105                        (to_tcl_name(e.name), name, float(loss))
1106        out+= "\n"
1107        for f in filters:
1108            out+= f(s)
1109    out+="$ns rtproto %s" % routing
1110    out+="""
1111$ns run
1112"""
1113    return out
1114
1115def topology_to_rspec(t, filters=[]):
1116    out = '<?xml version="1.0" encoding="UTF-8"?>\n' + \
1117        '<rspec xmlns="http://www.protogeni.net/resources/rspec/0.1"\n' + \
1118        '\txmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' + \
1119        '\txsi:schemaLocation="http://www.protogeni.net/resources/rspec/0.1 '+ \
1120        'http://www.protogeni.net/resources/rspec/0.1/request.xsd"\n' + \
1121        '\ttype="request" >\n'
1122
1123    ifname = { }
1124    ifnode = { }
1125
1126    for e in [e for e in t.elements if isinstance(e, Computer)]:
1127        name = e.name
1128        virt_type = e.get_attribute("virtualization_type") or "emulab-vnode"
1129        exclusive = e.get_attribute("exclusive") or "1"
1130        hw = e.get_attribute("type") or "pc";
1131        slots = e.get_attribute("slots") or "1";
1132        startup = e.get_attribute("startup")
1133
1134        extras = ""
1135        if startup: extras += '\t\tstartup_command="%s"\n' % startup
1136        out += '\t<node virtual_id="%s"\n\t\tvirtualization_type="%s"\n' % \
1137                (name, virt_type)
1138        out += '\t\texclusive="%s"' % exclusive
1139        if extras: out += '\n%s' % extras
1140        out += '>\n'
1141        out += '\t\t<node_type type_name="%s" slots="%s"/>\n' % (hw, slots)
1142        for i, ii in enumerate(e.interface):
1143            out += '\t\t<interface virtual_id="%s"/>\n' % ii.name
1144            ifnode[ii] = name
1145        for f in filters:
1146            out += f(e)
1147        out += '\t</node>\n'
1148
1149    for i, s in enumerate(t.substrates):
1150        if len(s.interfaces) == 0: 
1151            continue
1152        out += '\t<link virtual_id="%s" link_type="ethernet">\n' % s.name
1153        if s.capacity and s.capacity.kind == "max":
1154            bwout = True
1155            out += '\t\t<bandwidth>%d</bandwidth>\n' % s.capacity.rate
1156        else:
1157            bwout = False
1158        if s.latency and s.latency.kind == "max":
1159            out += '\t\t<latency>%d</latency>\n' % s.latency.time
1160        elif bwout:
1161            out += '\t\t<latency>0</latency>\n'
1162        for ii in s.interfaces:
1163            out += ('\t\t<interface_ref virtual_node_id="%s" ' + \
1164                    'virtual_interface_id="%s"/>\n') % (ifnode[ii], ii.name)
1165        for f in filters:
1166            out += f(s)
1167        out += '\t</link>\n'
1168    out += '</rspec>\n'
1169    return out
1170
Note: See TracBrowser for help on using the repository browser.