nsgenilib.py.in 28.1 KB
Newer Older
1 2
#!/usr/local/bin/python
#
3
# Copyright (c) 2005-2017 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
#
import sys
import getopt
import os, os.path
import pwd
import traceback
import string
import socket
import re
32
import math
33 34 35
import HTMLParser

# Configure stuff.
Leigh Stoller's avatar
Leigh Stoller committed
36
TB        = "@prefix@";
37 38
OURDOMAIN = "@OURDOMAIN@";

39 40 41 42 43 44
TBPATH = os.path.join(TB, "lib")
if TBPATH not in sys.path:
    sys.path.append(TBPATH)
    pass
import libdb

45
# Testbed specific stuff
46
if False:
Leigh Stoller's avatar
Leigh Stoller committed
47 48 49
    sys.path.append(TB + "/opsdir/lib/geni-lib")
else:
    sys.path.append("/usr/local/lib/geni-lib")
50 51 52 53 54

# Geni lib stuff.
import geni.portal as portal
import geni.rspec.pg as RSpec
import geni.rspec.igext as IG
55
import geni.rspec.emulab as emulab
56 57 58 59 60 61
import geni.urn as URN
import geni.namespaces as GNS

pc = portal.Context() 
rspec = RSpec.Request()
tour = IG.Tour()
Leigh Stoller's avatar
Leigh Stoller committed
62
routertype = None
63 64 65 66 67 68 69 70

# This is how we read the NS parser output XML.
from lxml import etree

def Fatal(message):
    print >> sys.stderr, message
    sys.exit(1)

Leigh Stoller's avatar
Leigh Stoller committed
71 72 73
def Warn(message):
    print >> sys.stderr, message

74 75 76 77 78
def Usage():
    print "usage: " + sys.argv[0] + " [option...] irfile"
    sys.exit(-1);
    pass

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
#
# Helper function to find find osname.
#
def LookupImage(osname):
    res = libdb.DBQueryFatal(
            "select i.imagename from images as i "
            "where i.pid=%s and i.imagename=%s",
            (PID, osname))

    if len(res) == 0:
        return "emulab-ops" + "//" + osname

    return PID + "//" + osname
    pass

94 95 96 97
if len(sys.argv) < 2:
    Usage();
    pass

98 99 100 101 102 103 104 105 106
#
# Check for PID flag.
# 
if len(sys.argv) > 2 and sys.argv[1] == "-p":
    PID = sys.argv[2]
    NSfile = sys.argv[3]
else:
    PID = None
    NSfile = sys.argv[1]
107 108 109

try:
    tree = etree.parse(NSfile);
Leigh Stoller's avatar
Leigh Stoller committed
110 111 112
except etree.XMLSyntaxError:
    print traceback.format_exc()
    Fatal("Could not parse IR file")
113 114 115
    pass

#
Leigh Stoller's avatar
Leigh Stoller committed
116
# First find the nodes and links and other things we need to start.
117
#
Leigh Stoller's avatar
Leigh Stoller committed
118 119
nodes       = {}
lans        = {}
120
lansets     = {}
Leigh Stoller's avatar
Leigh Stoller committed
121 122 123 124
ifaces      = {}
blockstores = {}
lanifaces   = {}
ifacecounts = {}
125
bridged     = {}
126
programs    = {}
127
firewall    = None
128

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
#
# Need to know which node is the experiment firewall.
#
for child in tree.getroot():
    if child.tag == "virt_firewalls":
        row = child.find("row")
        fwname = row.find("fwname").text
        style  = row.find("style").text
        if firewall:
            Fatal("Only one experiment firewall allowed");
            pass
        firewall = rspec.ExperimentFirewall(fwname, style);
        pass
    pass

#
# Now process nodes and a few other things.
#
147 148 149 150
for child in tree.getroot():
    if child.tag == "virt_nodes":
        row = child.find("row")
        vname = row.find("vname").text
151 152 153
        role  = ""
        if row.find("role") is not None:
            role = row.find("role").text
154 155 156 157 158
        #
        # We have to figure out first if we have a XEN VM, so look
        # at the type.
        #
        ntype = row.find("type").text
159 160 161 162
        if role == "bridge":
            node = IG.Bridge(vname, "eth0", "eth1");
            pass  
        elif ntype == "pcvm" or re.match(r".*\-vm$", ntype):
163 164 165 166
            node = IG.XenVM(vname)
            if ntype != "pcvm":
                node.xen_ptype = ntype;
                pass
167 168
            # Start out exclusive; might change below.
            node.exclusive = True
Leigh Stoller's avatar
Leigh Stoller committed
169
        elif ntype == "blockstore":
170
            node = IG.RemoteBlockstore(vname, "/mnt", "eth0")
Leigh Stoller's avatar
Leigh Stoller committed
171
            node.exclusive = True
172 173 174 175
        elif firewall and firewall.client_id == vname:
            # We created the firewall above.
            node = firewall;
            node.exclusive = True
176 177 178 179 180
        else:
            node = RSpec.RawPC(vname)
            node.hardware_type = ntype
            pass

181 182 183 184
        for element in row:
            #
            # We handle a subset of node things.
            #
185
            if element.tag == "osname" and element.text:
186 187
                #
                # Convert NS project/osname to rspec project//osname.
188
                #
189 190
                osname = element.text
                if osname.find("/") < 0:
191 192 193 194 195
                    if PID == None:
                        osname = "emulab-ops//" + osname
                    else:
                        osname = LookupImage(osname);
                        pass
196 197 198 199
                elif osname.find("//") < 0:
                    osname = osname.replace("/", "//");
                    pass
                node.disk_image = "urn:publicid:IDN+" + OURDOMAIN + "+image+" + osname
Leigh Stoller's avatar
Leigh Stoller committed
200
            elif element.tag == "ips" and element.text != None:
201 202 203
                ips = element.text.split()
                for token in ips:
                    vport,ip = token.split(":")
204 205
                    # Bridges create interfaces internally, we want to use those.
                    if role == "bridge":
Leigh Stoller's avatar
Leigh Stoller committed
206 207 208 209 210
                        if vport == "0":
                            iface = node.iface0
                        else:
                            iface = node.iface1
                            pass
211 212 213 214 215
                    elif ntype == "blockstore":
                        iface = node.interface
                    else:
                        iface = node.addInterface("eth" + vport)
                    
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
                    iface.addAddress(RSpec.IPv4Address(ip, "255.255.255.0"))
                    # This is the "member" field in virt_lan.
                    ifaces[vname + ":" + vport] = iface
                    pass
            elif element.tag == "tarfiles" and element.text:
                tarfiles = element.text.split(";")
                for token in tarfiles:
                    directory,filename = token.split()
                    node.addService(RSpec.Install(filename,directory))
                    pass
                pass
            elif element.tag == "failureaction" and element.text == "nonfatal":
                raw = etree.Element("{%s}failure_action" %
                                    (RSpec.Namespaces.EMULAB.name))
                raw.attrib["action"] = "nonfatal"
                node.addRawElement(raw)
                pass
Leigh Stoller's avatar
Leigh Stoller committed
233 234 235 236 237 238
            elif element.tag == "routertype" and element.text:
                routertype = element.text
                if routertype == "static-ddijk":
                    routertype = "static"
                    pass
                pass
239
            elif (element.tag in ["loadlist", "cmd_line", "startupcmd"] and
240 241 242
                  element.text and element.text != ""):
                Fatal("Unsupported attribute on node " +
                      vname + ": " + element.tag + " - " + element.text)
243
                pass
244 245
            pass
        nodes[vname] = node
246 247 248
        if not (firewall and firewall.client_id == vname):
            rspec.addResource(node)
            pass
249
        pass
Leigh Stoller's avatar
Leigh Stoller committed
250 251 252 253 254 255 256 257 258 259 260 261 262
    #
    # We need to know how many members, so we can create a geni-lib Link or LAN.
    # This seems totally wrong.
    #
    if child.tag == "virt_lans":
        row = child.find("row")
        vname  = row.find("vname").text
        member = row.find("member").text
        if not vname in ifacecounts:
            ifacecounts[vname] = 0
            pass
        ifacecounts[vname] = ifacecounts[vname] + 1
        pass
263
    #
264 265 266 267 268 269 270 271 272 273
    # These are not supported, warn early.
    #
    if child.tag == "event_groups":
        row = child.find("row")
        group_name = row.find("group_name").text
        if group_name != "__all_programs":
            Fatal("Unsupported use of event groups")
            pass
        pass
    #
274 275 276 277 278 279 280 281 282 283 284
    # Find the program agents before we try to process the event list.
    # The event list is how we tell the difference between a startcmd
    # and other program agents. 
    #
    if child.tag == "virt_programs":
        row = child.find("row")
        vnode = row.find("vnode").text
        vname = row.find("vname").text
        cmd   = row.find("command").text
        directory = row.find("dir").text
        #
285 286 287 288 289 290
        # Watch for old use of ops program agents, not supported.
        #
        if vnode == "ops":
            Fatal("Unsupported use of program agents on ops")
            pass
        #
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
        # Strip out the startcmd stuff, we want just the command.
        #
        foo = re.match(r"^\((.*) ; /usr/local/etc/emulab.*\)", cmd);
        if foo:
            parser = HTMLParser.HTMLParser()
            cmd = parser.unescape(foo.group(1));
            pass
        #
        # Watch for access to builting Emulab ENV variables, those
        # are not supported when converting.
        #
        for var in ["PID", "EID", "EXPDIR", "NODE", "NODEIP",
                    "NODECNET", "NODECNETIP"]:
            if (re.search("\$" + var + "[^\w]", cmd, re.MULTILINE) or
                re.search("\$" + var + "$", cmd, re.MULTILINE)):
                Fatal("Unsupported use of environment variable $" +
                      var + " in startup command or program agent")
                pass
            pass
        programs[vname] = {"vname" : vname, "vnode" : vnode,
                           "cmd" : cmd, "directory" : directory,
                           # We will set this when we process the event list.
                           "onexpstart" : False}
        pass
Leigh Stoller's avatar
Leigh Stoller committed
315 316
    pass

Leigh Stoller's avatar
Leigh Stoller committed
317 318 319 320 321 322
#
# There is only one of these, at top level.
#
if routertype:
    rspec.setRoutingStyle(routertype)

Leigh Stoller's avatar
Leigh Stoller committed
323 324 325 326
#
# Now we can create Link or LANs cause we know the iface counts. Dumb.
#
for child in tree.getroot():
327 328 329
    if child.tag == "virt_lan_lans":
        row = child.find("row")
        vname = row.find("vname").text
Leigh Stoller's avatar
Leigh Stoller committed
330 331 332 333 334
        if ifacecounts[vname] == 2:
            lan = RSpec.Link(vname)
        else:
            lan = RSpec.LAN(vname)
            pass
335
        lans[vname] = lan;
336
        lansets[vname] = {}
337 338
        rspec.addResource(lan)
        pass
339 340 341 342 343 344 345 346
    if child.tag == "virt_bridges":
        row = child.find("row")
        vname = row.find("vname").text
        vlink = row.find("vlink").text
        vport = row.find("vport").text
        # record that these links are bridged by bridge
        bridged[vlink] = {"bridge" : vname, "vport" : vport}
        pass
347 348 349 350 351 352 353 354
    if child.tag == "virt_nodes":
        row = child.find("row")
        vname = row.find("vname").text
        for element in row:
            if element.tag == "fixed" and element.text != None:
                #
                # Watch for binding to another node in the topology.
                #
355
                if element.text in nodes:
356 357
                    nodes[vname].InstantiateOn(nodes[element.text])
                else:
358
                    nodes[vname].component_id = URN.Node(OURDOMAIN,element.text)
359 360
    pass

Leigh Stoller's avatar
Leigh Stoller committed
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
#
# Look for blockstores, to attach to nodes.
# 
for child in tree.getroot():
    if child.tag == "virt_blockstores":
        row = child.find("row")
        vname = row.find("vname").text
        size  = row.find("size").text
        fixed = row.find("fixed").text
        node  = nodes[fixed];
        if node.type and node.type == "emulab-blockstore":
            #
            # A remote blockstore.
            #
            bs = node._bs
        else:
            #
            # A local blockstore.
            #
            bs = node.Blockstore(vname, "/mnt")
            pass
        blockstores[vname] = bs
383 384 385 386 387 388 389 390 391

        if row.find("size") is not None:
            size = int(row.find("size").text)
            if size:
                # Ug, Geni wants this in GB.
                size = int(math.ceil(size * 0.001048576))
                bs.size = str(size) + "GB"
                pass
            pass
Leigh Stoller's avatar
Leigh Stoller committed
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
        pass
    pass

#
# Now we can do the blockstore attributes.
#
for child in tree.getroot():
    if child.tag == "virt_blockstore_attributes":
        row = child.find("row")
        vname = row.find("vname").text
        bs    = blockstores[vname]
        key   = row.find("attrkey").text
        val   = row.find("attrvalue").text
        if key == "readonly" and val == "1":
            bs.readonly = True;
            pass
        if key == "class":
            # XXX The CM turns remote into whatever it should be.
            if val != "local":
                val = "remote"
                pass
            bs.where = val;
            pass
        if key == "dataset":
            #XXX
417 418 419
            tokens = val.split("/");
            bs.dataset = "urn:publicid:IDN+" + OURDOMAIN + ":" + tokens[0]
            bs.dataset = bs.dataset + "+dataset+" + tokens[1]
Leigh Stoller's avatar
Leigh Stoller committed
420 421 422
            pass
        if key == "leasename":
            #XXX
423 424 425
            tokens = val.split("/");
            bs.dataset = "urn:publicid:IDN+" + OURDOMAIN + ":" + tokens[0]
            bs.dataset = bs.dataset + "+dataset+" + tokens[1]
Leigh Stoller's avatar
Leigh Stoller committed
426 427 428 429 430 431 432 433 434 435
            pass
        if key == "mountpoint":
            bs.mount = val;
            pass
        if key == "placement":
            bs.placement = val;
            pass
        pass
    pass

436
#
437 438 439 440 441 442
# Now we process virt_lans, with the links and interfaces we created
# above. But the wrinkle is that we have to treat links and lans
# differently, since the rspec format wants the original source/dest
# parameters for each direction, which was lost when the links were
# converted into virt_lans. We end having to gather up all the interfaces,
# and then doing a bit of what libvtop does.
443 444 445 446 447 448 449 450 451 452 453 454 455 456
#
for child in tree.getroot():
    if child.tag == "virt_lans":
        row = child.find("row")
        vname  = row.find("vname").text
        member = row.find("member").text
        lan    = lans[vname]
        iface  = ifaces[member]
        mask   = row.find("member").text
        lan.addInterface(iface)
        #
        # A lot of these things are per virt_lan, but they are really
        # for the entire lan. 
        #
457
        mask = row.find("mask").text
458 459
        iface.netmask = mask;

460 461 462 463 464 465 466 467
        #
        # Gather up interfaces per lan.
        #
        if not vname in lanifaces:
            lanifaces[vname] = {}
            pass
        lanifaces[vname][member] = row;

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
        if row.find("trivial_ok") != None:
            trivial_ok = int(row.find("trivial_ok").text)
            if trivial_ok:
                lan.trivial_ok = True
                pass
            pass
        if row.find("encap_style") != None:
            encap_style = row.find("encap_style").text
            if encap_style == "vlan":
                lan.vlan_tagging = True
                pass
            pass
        if row.find("emulated") != None:
            emulated = int(row.find("emulated").text)
            if emulated:
483
                lan.link_multiplexing = True
484 485
                pass
            pass
486 487 488
        if row.find("nobwshaping") != None:
            nobwshaping = int(row.find("nobwshaping").text)
            if nobwshaping:
489 490 491 492
                if not lansets[vname].has_key("nobwshaping"):
                    lan.setNoBandwidthShaping()
                    pass
                lansets[vname]["nobwshaping"] = True
493 494
                pass
            pass
495 496 497
        if row.find("mustdelay") != None:
            mustdelay = int(row.find("mustdelay").text)
            if mustdelay:
498 499 500 501
                if not lansets[vname].has_key("mustdelay"):
                    lan.setForceShaping()
                    pass
                lansets[vname]["mustdelay"] = True
502 503
                pass
            pass
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
        if row.find("protocol") != None:
            protocol = row.find("protocol").text
            if protocol != "ethernet":
                lan.protocol = protocol
                pass
            pass
        if row.find("fixed_iface") != None:
            fixed_iface = row.find("fixed_iface").text
            fixed_urn = "urn:publicid:IDN+" + OURDOMAIN + "+ignore+" + fixed_iface
            iface.component_id = fixed_iface
            pass
        if row.find("ip_aliases") != None:
            Fatal("Unsupported use of ip aliases in lan " + vname)
            pass
        pass
    pass

for lanname,lifaces in lanifaces.iteritems():
Leigh Stoller's avatar
Leigh Stoller committed
522
    skip = 0
523 524
    nobwshaping = 1;
    
Leigh Stoller's avatar
Leigh Stoller committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538
    #
    # Yuck, the parser spits out a min bw for blockstore links/lans.
    # But we want to kill that and let the best effort setting take care of it.
    #
    for member_key in lifaces.keys():
        # Really, a row.
        member = lifaces[member_key]
        vnode  = member.find("vnode").text
        node   = nodes[vnode]
        if node.type and node.type == "emulab-blockstore":
            skip = 1
        pass
    if skip:
        continue
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
    if len(lifaces.keys()) == 2:
        lan = lans[lanname]
        member0_key = lifaces.keys()[0]
        member1_key = lifaces.keys()[1]
        member0     = lifaces[member0_key]
        member1     = lifaces[member1_key]
        iface0      = ifaces[member0_key]
        iface1      = ifaces[member1_key]
        bw0         = int(member0.find("bandwidth").text)
        rbw0        = int(member0.find("rbandwidth").text)
        delay0      = float(member0.find("delay").text)
        rdelay0     = float(member0.find("rdelay").text)
        loss0       = float(member0.find("lossrate").text)
        rloss0      = float(member0.find("rlossrate").text)
        bw1         = int(member1.find("bandwidth").text)
        rbw1        = int(member1.find("rbandwidth").text)
        delay1      = float(member1.find("delay").text)
        rdelay1     = float(member1.find("rdelay").text)
        loss1       = float(member1.find("lossrate").text)
        rloss1      = float(member1.find("rlossrate").text)

Leigh Stoller's avatar
Leigh Stoller committed
560 561
        #print member0_key,bw0,delay0,loss0,member1_key,bw1,delay1,loss1

562 563 564 565 566 567 568 569
        #
        # The virt lans for bridges is done differently. 
        #
        if bridged.has_key(lanname):
            bname  = bridged[lanname]["bridge"]
            bport  = bridged[lanname]["vport"]
            bridge = nodes[bname]
            memb   = bname + ":" + bport
Leigh Stoller's avatar
Leigh Stoller committed
570 571 572 573
            if bport == "0":
                pipe   = bridge.pipe0
            else:
                pipe   = bridge.pipe1
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588

            #print bname,bport,memb,str(pipe),member0_key,member1_key

            if member0_key == memb:
                pipe.bandwidth = bw0
                pipe.latency   = delay0
                pipe.lossrate  = loss0
                pass
            if member1_key == memb:
                pipe.bandwidth = bw1
                pipe.latency   = delay1
                pipe.lossrate  = loss1
                pass
            continue;
        
589 590 591 592 593 594 595 596
        # These are the bi-directional numbers.
        delay       = delay0+rdelay1
        loss        = 1-(1-loss0)*(1-rloss1)
	bw          = min(bw0,rbw1)
        rdelay      = rdelay0+delay1
	rloss       = 1-(1-rloss0)*(1-loss1)
	rbw         = min(rbw0,bw1)

597 598 599 600 601 602 603 604
        #
        # If the user has simply requested a standard 1G or 10G link,
        # then force BW shaping off so that the CM will do a normal
        # assignment. 
        #
        if (delay != 0 or loss != 0 or rdelay != 0 or rloss != 0 or
            ((bw != 1000000 and bw != 10000000) or
             (rbw != 1000000 and rbw != 10000000))):
605
            nobwshaping = 0;
606 607
            pass

608 609 610 611 612 613 614
        # geni-lib puts shaping params on both ifaces and links.
        iface0.bandwidth = bw
        iface0.latency   = delay
        iface0.plr       = loss
        iface1.bandwidth = rbw
        iface1.latency   = rdelay
        iface1.plr       = rloss
615 616 617
        if nobwshaping:
            lan.setNoBandwidthShaping();
            pass
618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
    else:
        #
        # A symmetrically shaped lan is the most common and easiest to deal
        # with. Note that rspecs are more like the virt_lan representation,
        # in that each member specifies shaping params *to the lan*. However,
        # rspecs do not support shaping params *from the lan*. So bottom line
        # is that we can do fully symmetric and per-node, but not asymmetric
        # on an individual node basis.
        #
        lan = lans[lanname]
        for member_key in lifaces.keys():
            member   = lifaces[member_key]
            iface    = ifaces[member_key]
            bw       = int(member.find("bandwidth").text)
            rbw      = int(member.find("rbandwidth").text)
            delay    = float(member.find("delay").text)
            rdelay   = float(member.find("rdelay").text)
            loss     = float(member.find("lossrate").text)
            rloss    = float(member.find("rlossrate").text)

            if bw != rbw or delay != rdelay or loss != rloss:
                Fatal("asymmetric shaping not supported on lan " + lanname);
                pass

642 643 644 645 646 647 648 649 650 651 652
            #
            # If the user has simply requested a standard 1G or 10G link,
            # then force BW shaping off so that the CM will do a normal
            # assignment.
            #
            if (delay != 0 or loss != 0 or
                ((bw != 1000000 and bw != 10000000) or
                 (rbw != 1000000 and rbw != 10000000))):
                nobwshaping = 0;
                pass

653 654 655 656 657
            # geni-lib puts shaping params on both ifaces and links.
            iface.bandwidth = bw
            iface.latency   = delay
            iface.plr       = loss
            pass
658 659 660
        if nobwshaping:
            lan.setNoBandwidthShaping();
            pass
661 662 663 664
        pass
    pass

#
665
# Other various things that are in the NS file, that we can handle (or not).
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
#
for child in tree.getroot():
    if child.tag == "portal":
        row = child.find("row")
        for element in row:
            if element.tag == "description":
                tour.Description(tour.TEXT, element.text)
            elif element.tag == "instructions":
                tour.Instructions(tour.TEXT, element.text)
                pass
            pass
        rspec.addTour(tour)
        pass
    if child.tag == "experiments":
        row = child.find("row")
681 682 683 684 685
        if row.find("encap_style") != None:
            encap_style = row.find("encap_style").text
            if encap_style == "vlan":
                for name,lan in lans.iteritems():
                    lan.vlan_tagging = True
686 687
                    pass
                pass
688 689 690 691 692 693 694 695 696
            pass
        if row.find("multiplex_factor") != None:
            factor = int(row.find("multiplex_factor").text)
            rspec.setCollocateFactor(factor)
            pass
        if row.find("packing_strategy") != None:
            strategy = row.find("packing_strategy").text
            rspec.setPackingStrategy(strategy)
            pass
Leigh Stoller's avatar
Leigh Stoller committed
697 698 699 700 701 702 703 704 705 706 707 708 709
        if row.find("delay_osname") != None:
            #
            # Convert NS project/osname to rspec project//osname.
            # But if no project, add emulab-ops (clearly wrong).
            osname = row.find("delay_osname").text
            if osname.find("/") < 0:
                osname = "emulab-ops//" + osname
            elif osname.find("//") < 0:
                osname = osname.replace("/", "//");
                pass
            disk_image = "urn:publicid:IDN+" + OURDOMAIN + "+image+" + osname
            rspec.setDelayImage(disk_image)
            pass
710 711
        #
        # A bunch of things we do not support yet.
712
        # We ignore security_level for now, it gets set for an explicit firewall.
713
        #
714
        for tag in ["jail_osname"]:
715 716 717 718 719 720 721 722 723 724
            if row.find(tag) != None:
                Fatal("Unsupported use of " + tag)
                pass
            pass
        for tag in ["forcelinkdelays", "nonfsmounts"]:
            if row.find(tag) != None:
                tmp = int(row.find(tag).text)
                if tmp:
                    Fatal("Unsupported use of " + tag)
                    pass
725 726 727 728
                pass
            pass
        pass
    #
729 730 731
    # We only consider program agents. We want to find any that have time=0
    # and mark the program with onexpstart=True. We spit out the program
    # agent list below.
732
    #
Leigh Stoller's avatar
Leigh Stoller committed
733
    if child.tag == "eventlist":
734
        row = child.find("row")
Leigh Stoller's avatar
Leigh Stoller committed
735 736
        otype = row.find("objecttype").text
        etype = row.find("eventtype").text
737 738
        vnode = row.find("vnode").text
        vname = row.find("vname").text
739 740 741 742
        if row.find("time") == None:
            Fatal("No start time provided for program object " + vname)
            pass
        time  = row.find("time").text
743 744 745 746
        if otype == "4":
            if etype != "1" and etype != "20":
                Fatal("Unsupported program object event for " + vname)
                pass
747
            if time != "0" and time != "0.0":
748 749
                Fatal("Unsupported program object event time!=0 for " + vname)
                pass
750
            programs[vname]["onexpstart"] = True
751 752
        else:
            Fatal("Unsupported event type for " + vname)
753 754 755 756 757 758 759 760 761
            pass
        pass
    #
    # Watch for desires that specify a shared node.
    #
    if child.tag == "virt_node_desires":
        row = child.find("row")
        vname  = row.find("vname").text
        desire = row.find("desire").text
762
        weight = row.find("weight").text
763 764
        if desire == "pcshared":
            nodes[vname].exclusive = False
765 766 767 768 769
        else:
            raw = etree.Element("{%s}fd" % (RSpec.Namespaces.EMULAB.name))
            raw.attrib["name"]   = desire
            raw.attrib["weight"] = weight
            nodes[vname].addRawElement(raw)
770 771
            pass
        pass
772 773 774 775 776
    if child.tag == "virt_node_attributes":
        row = child.find("row")
        vname  = row.find("vname").text
        key    = row.find("attrkey").text
        val    = row.find("attrvalue").text
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801
        node   = nodes[vname]

        #
        # Many node attributes, we have to translate some, the rest
        # are passed through.
        #
        if key == "routable_control_ip":
            if val.lower() == "true":
                node.routable_control_ip = True
                pass
        elif ((key == "MEMORY_SIZE" or key == "XEN_MEMSIZE") and
              node.type == "emulab-xen"):
            node.ram = int(val) / 1024
        elif ((key == "XEN_CORES" or key == "VM_CPUS") and
              node.type == "emulab-xen"):
            node.cores = int(val)
        elif key == "XEN_EXTRAFS" and node.type == "emulab-xen":
            node.disk = int(val)
        else:
            raw = etree.Element("{%s}node_attribute" %
                                (RSpec.Namespaces.EMULAB.name))
            raw.attrib["key"]   = key
            raw.attrib["value"] = val
            node.addRawElement(raw)
            pass
802
        pass
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819
    if child.tag == "firewall_rules":
        #
        # Note that we are ignoring the security level. Not really sure if it
        # makes any sense these days. The CM will set it if needed.
        #
        row = child.find("row")
        fwname = row.find("fwname").text
        fwrule = row.find("rule").text
        ruleno = row.find("ruleno").text
        if not fwname in nodes:
            Fatal("No such firwall node " + fwname);
            pass
        if not firewall:
            Fatal("Firewall rule for nonexistent firewall " + fwname);
            pass
        firewall.addRule(fwrule);
        pass
820 821 822
    if child.tag == "virt_trafgens":
        Fatal("Trafgens are not supported anymore")
        pass
Leigh Stoller's avatar
Leigh Stoller committed
823 824 825
    if child.tag == "virt_user_environment":
        Fatal("opt variables are not supported when converting to geni-lib")
        pass
826 827
    if child.tag in ["virt_lan_settings",
                     "virt_lan_member_settings", "virt_routes",
Leigh Stoller's avatar
Leigh Stoller committed
828
                     "virt_node_disks",
829 830 831 832 833 834
                     "virt_tiptunnels", "elabinelab_attributes",
                     "virt_paths", "experiment_blobs", "virt_blobs",
                     "virt_client_service_ctl", "virt_client_service_hooks",
                     "virt_client_service_opts", "virt_address_allocation"]:
        Fatal("Unsupported use of " + child.tag)
        pass
835 836
    pass

837 838 839 840 841 842 843 844 845 846 847 848
#
# Add the rest of the program agents
#
for vname, program in programs.iteritems():
    cmd = program["cmd"]
    directory = program["directory"]
    node = program["vnode"]
    onexpstart = program["onexpstart"]
    nodes[node].addService(emulab.ProgramAgent(vname, cmd,
                                                directory, onexpstart));
    pass;

849
pc.printRequestRSpec(rspec)