diff --git a/tmcd/myplc/bootmanager.patch b/tmcd/myplc/bootmanager.patch
new file mode 100644
index 0000000000000000000000000000000000000000..91bd56c772eaaaac2b238f461027a26ac179c4f8
--- /dev/null
+++ b/tmcd/myplc/bootmanager.patch
@@ -0,0 +1,24 @@
+diff -ur source/configuration source.new/configuration
+--- source/configuration	2007-03-12 00:18:05.000000000 -0700
++++ source.new/configuration	2007-03-12 00:19:30.000000000 -0700
+@@ -52,7 +52,7 @@
+ 
+ 
+ # whether or not to skip hardware requirement check
+-SKIP_HARDWARE_REQUIREMENT_CHECK=0
++SKIP_HARDWARE_REQUIREMENT_CHECK=1
+ 
+ 
+ # minimum amount of memory needed for installer, in kb
+diff -ur source/steps/ConfirmInstallWithUser.py source.new/steps/ConfirmInstallWithUser.py
+--- source/steps/ConfirmInstallWithUser.py	2007-03-12 00:18:05.000000000 -0700
++++ source.new/steps/ConfirmInstallWithUser.py	2007-03-12 00:21:41.000000000 -0700
+@@ -47,7 +47,7 @@
+     log.write( "\n\nStep: Confirming install with user.\n" )
+     
+     try:
+-        confirmation= ""
++        confirmation= "yes"
+         install= 0
+         print welcome_message
+         
diff --git a/tmcd/myplc/libplcsetup.py b/tmcd/myplc/libplcsetup.py
new file mode 100644
index 0000000000000000000000000000000000000000..73606b57a3b78dde09aa3f4807af3e02f30f0b2d
--- /dev/null
+++ b/tmcd/myplc/libplcsetup.py
@@ -0,0 +1,520 @@
+#!/usr/bin/python
+
+import sys
+import os
+import os.path
+import socket
+import random
+import traceback
+import xmlrpclib
+# grab plc_config
+sys.path.append('/plc/root/usr/lib/python2.4/site-packages')
+from plc_config import PLCConfiguration
+
+
+debug = 1
+cachedRootUID = None
+cachedRootPasswd = None
+
+DEF_ROOT_UID = 'root@localhost.localdomain'
+DEF_ROOT_ACCOUNT_INFO_FILE = '/etc/planetlab/rootacctinfo'
+DEF_PLC_CONFIG_FILE = '/plc/data/etc/planetlab/plc_config.xml'
+DEF_PLC_URL = 'https://localhost/PLCAPI/'
+DEF_SITE_ID = 1
+
+def plcReadConfigVar(catID,varID,configFile=DEF_PLC_CONFIG_FILE):
+    """
+    Returns the value of the specified variable.
+    """
+    plcc = PLCConfiguration(configFile)
+    ret = plcc.get(catID,varID)
+    if ret == None:
+        return None
+
+    for d in ret:
+        if d['id'] == varID:
+            ret = d['value']
+            break
+        pass
+
+    return ret
+
+def plcUpdateConfig(variables,configFile=DEF_PLC_CONFIG_FILE):
+    """
+    Update the default planetlab config file.
+    Arguments:
+      variables = list of dicts or lists
+      configFile = alternate planetlab config file
+
+      Example: to set plc_www/host (i.e., PLC_WWW_HOST in myplc docs), do
+
+      variables[0] = { 'category' : 'plc_www',
+                       'variable' : 'host',
+                       'value'    : <value> }
+        or
+      variables[0] = [ 'plc_www','host',<value> ]
+    """
+    
+    plcc = PLCConfiguration(configFile)
+    
+    for obj in variables:
+        catDict = dict()
+        varDict = dict()
+        
+        if type(obj) == list:
+            catDict['id'] = obj[0]
+            varDict['id'] = obj[1]
+            varDict['value'] = obj[2]
+            pass
+        elif type(obj) == dict:
+            catDict['id'] = obj['category']
+            varDict['id'] = obj['variable']
+            varDict['value'] = obj['value']
+            pass
+        else:
+            raise "Unsupported variable object!"
+        
+        plcc.set(catDict,varDict)
+        pass
+    
+    plcc.save()
+    pass
+
+def getXMLRPCAuthInfo():
+    readRootAcctInfo()
+    
+    auth = { 'Username'   : cachedRootUID,
+             'AuthString' : cachedRootPasswd,
+             'AuthMethod' : 'password',
+             'Role'       : 'admin' }
+
+    return auth
+
+def getXMLRPCServer(url=DEF_PLC_URL):
+    return xmlrpclib.Server(url,allow_none=True)
+
+def plcAddUser(realname,email,passwd,keys=[],root=False):
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    (fn,ln) = (None,None)
+    sn = realname.split(' ')
+    if len(sn) >= 2:
+        (fn,ln) = (sn[0],sn[-1])
+        pass
+    else:
+        (fn,ln) = (sn[0],'EmulabFamily')
+        pass
+
+    pid = server.AddPerson(auth,{ 'first_name' : fn,
+                                  'last_name'  : ln,
+                                  'url'        : 'http://www.emulab.net',
+                                  # "saw your name and number on the wall..."
+                                  'phone'      : '867-5309',
+                                  'password'   : passwd,
+                                  'email'      : email })
+
+    server.UpdatePerson(auth,pid,{ 'enabled' : True })
+
+    for key in keys:
+        # have to catch exceptions here cause plc only allows ssh2 keys,
+        # and users could have *anything* in their authorized_keys
+        try:
+            server.AddPersonKey(auth,pid,{ 'key_type' : 'ssh',
+                                           'key'      : key })
+        except:
+            pass
+        pass
+
+    if root:
+        server.AddRoleToPerson(auth,'admin',pid)
+        pass
+    
+    return pid
+
+def plcDeleteUser(email):
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    return server.DeletePerson(auth,email)
+
+def plcUpdateUser(realname,email,passwd,keys=[],root=False):
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    ulist = server.GetPersons(auth,
+                              { 'email' : email },
+                              [ 'first_name','last_name','roles','key_ids' ])
+
+    if len(ulist) == 0:
+        print "WARNING: could not find match for user %s!" % email
+        return 0
+    elif len(ulist) > 1:
+        print "WARNING: more than one match for user %s, using first" % email
+        pass
+    
+    (fn,ln) = (None,None)
+    sn = realname.split(' ')
+    if len(sn) >= 2:
+        (fn,ln) = (sn[0],sn[-1])
+        pass
+    else:
+        (fn,ln) = (sn[0],'EmulabFamily')
+        pass
+
+    # update name
+    if fn != ulist[0]['first_name'] or ln != ulist[0]['last_name']:
+        server.UpdatePerson(auth,email,{ 'first_name' : fn,
+                                         'last_name'  : ln })
+        pass
+
+    # update roles
+    if 'admin' in ulist[0]['roles'] and not root:
+        server.UpdatePerson(auth,email,{ 'roles' : [ 'user' ] })
+        pass
+
+    # update keys
+    retlist = server.GetKeys(auth,ulist[0]['key_ids'],[ 'key_id','key' ])
+    keylist = map(lambda(x): x['key'],retlist)
+    # add:
+    for rk in keys:
+        if not rk in keylist:
+            # have to catch exceptions here cause plc only allows ssh2 keys,
+            # and users could have *anything* in their authorized_keys
+            try:
+                server.AddPersonKey(auth,email,{ 'key_type' : 'ssh',
+                                                 'key'      : rk })
+            except:
+                pass
+            pass
+        pass
+    # delete:
+    for ret in retlist:
+        if not ret['key'] in keylist:
+            server.DeletePersonKey(auth,ret['key_id'])
+            pass
+        pass
+
+    # always update the password, it's easier than doing AuthCheck and all...
+    server.UpdatePerson(auth,email,{ 'password' : passwd })
+    
+    return 1
+
+def plcUpdateUsers(uplist=[]):
+    """
+    Takes a list of (realname,email,passwd,keys=[],root=False) account tuples
+    and adds, deletes, and updates as needed.
+    """
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    userlist = server.GetPersons(auth,{},[ 'email' ])
+
+    # go through the uplist and ulist and figure out what needs
+    # adding, deleting, or updating:
+    alist = list() # list of tuples from uplist
+    dlist = list() # list of hostnames to be deleted
+    ulist = list() # list of tuples from uplist
+
+    for ntuple in uplist:
+        found = False
+        for user in userlist:
+            if ntuple[1] == user['email']:
+                found = True
+                break
+            pass
+        if found:
+            ulist.append(ntuple)
+            pass
+        else:
+            alist.append(ntuple)
+            pass
+        pass
+
+    for user in userlist:
+        found = False
+        for ntuple in uplist:
+            if ntuple[1] == user['email']:
+                found = True
+                break
+            pass
+        if not found and user['email'].endswith('emulab.net'):
+            dlist.append(user['email'])
+            pass
+        pass
+
+    #print "alist = %s\n\nulist = %s\n\ndlist = %s\n" % (alist,ulist,dlist)
+    
+    for als in alist:
+        print "Adding user %s" % als[1]
+        plcAddUser(*als)
+        pass
+
+    for uls in ulist:
+        print "Updating user %s" % uls[1]
+        plcUpdateUser(*uls)
+        pass
+
+    for dls in dlist:
+        print "Deleting user %s" % dls
+        plcDeleteUser(dls)
+        pass
+
+    return None
+
+def addMACDelim(mac,delim=':'):
+    if mac.count(delim) == 0:
+        return mac[0:2] + delim + mac[2:4] + delim + mac[4:6] + delim + \
+               mac[6:8] + delim + mac[8:10] + delim + mac[10:12]
+    else:
+        return mac
+    pass
+
+def plcAddNode(hostname,ip,mac):
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    fmac = addMACDelim(mac,':')
+    
+    nid = server.AddNode(auth,DEF_SITE_ID,{ 'hostname' : hostname })
+
+    nnid = server.AddNodeNetwork(auth,nid,{ 'is_primary' : True,
+                                            'hostname'   : hostname,
+                                            'ip'         : ip,
+                                            'mac'        : fmac,
+                                            'method'     : 'dhcp',
+                                            'type'       : 'ipv4' })
+
+    # ugh, have to set a node key manually
+    astr = "abcdefghijklmnopqrstuvwxyz"
+    rstr = astr + astr.upper() + '0123456789'
+    tpasslist = random.sample(rstr,32)
+    tkey = ''
+    for char in tpasslist:
+        tkey += char
+        pass
+
+    server.UpdateNode(auth,nid,{ 'key' : tkey })
+    
+    return (nid,nnid)
+
+def plcDeleteNode(hostname):
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    return server.DeleteNode(auth,hostname)
+
+def plcUpdateNode(hostname,ip,mac):
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    fmac = addMACDelim(mac,':')
+
+    nodelist = server.GetNodes(auth,
+                               { 'hostname' : hostname },
+                               [ 'nodenetwork_ids' ])
+
+    if len(nodelist) != 1:
+        print "ERROR: more than one node %s found!" % hostname
+        return -1
+
+    if len(nodelist[0]['nodenetwork_ids']) != 1:
+        print "WARNING: more than one node network for %s; " + \
+              "using first!" % hostname
+        pass
+
+    nnid = nodelist[0]['nodenetwork_ids'][0]
+
+    nnlist = server.GetNodeNetworks(auth,[ nnid ],[ 'ip','mac' ])
+
+    if len(nnlist) != 1:
+        print "WARNING: more than one node network for %s; " + \
+              "using first!" % hostname
+        pass
+    
+    if ip != nnlist[0]['ip'] or fmac != nnlist[0]['mac']:
+        return server.UpdateNodeNetwork(auth,nnid,{ 'ip' : ip,
+                                                    'mac' : fmac })
+        pass
+
+    # even if we don't do anything, return 1.
+    return 1
+
+def plcUpdateNodes(uplist=[]):
+    """
+    uplist should be a list of (hostname,ip,mac) tuples.
+    """
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    pnodelist = server.GetNodes(auth,{},
+                                [ 'hostname','nodenetwork_ids','node_id' ])
+    #pnetlist = server.GetNodeNetworks(auth,{},[ 'ip','hostname','mac' ])
+
+    # go through the uplist and pnodelist/pnetlist and figure out what needs
+    # adding, deleting, or updating:
+    alist = list() # list of tuples from uplist
+    dlist = list() # list of hostnames to be deleted
+    ulist = list() # list of tuples from uplist
+
+    for ntuple in uplist:
+        found = False
+        for pnode in pnodelist:
+            if ntuple[0] == pnode['hostname']:
+                found = True
+                break
+            pass
+        if found:
+            ulist.append(ntuple)
+            pass
+        else:
+            alist.append(ntuple)
+            pass
+        pass
+
+    for pnode in pnodelist:
+        found = False
+        for ntuple in uplist:
+            if ntuple[0] == pnode['hostname']:
+                found = True
+                break
+            pass
+        if not found:
+            dlist.append(ntuple[0])
+            pass
+        pass
+
+    #print "alist = %s\nulist = %s\ndlist = %s" % (alist,ulist,dlist)
+
+    for als in alist:
+        print "Adding node %s" % als[0]
+        plcAddNode(*als)
+        pass
+
+    for uls in ulist:
+        print "Updating node %s" % uls[0]
+        plcUpdateNode(*uls)
+        pass
+
+    for dls in dlist:
+        print "Deleting node %s" % dls
+        plcDeleteNode(dls)
+        pass
+
+    return None
+
+# XXX: what to do when we delete a node on one swapmod, then later re-add it?
+# Will both nids hang around in the db?
+def plcGetNodeID(hostname):
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+
+    nodelist = server.GetNodes(auth,{ 'hostname' : hostname },[ 'node_id' ])
+    if len(nodelist) > 0:
+        return nodelist[0]['node_id']
+    else:
+        raise "Could not find node_id for %s" % hostname
+
+    return None
+
+def plcGetNodeConfig(hostname):
+    """
+    Returns a list of strings that should be lines in a node config file.
+    """
+    auth = getXMLRPCAuthInfo()
+    server = getXMLRPCServer()
+    
+    nlist = server.GetNodes(auth,
+                               { 'hostname' : hostname },
+                               [ 'hostname','node_id','key' ])
+
+    if len(nlist) != 1:
+        return []
+
+    nnlist = server.GetNodeNetworks(auth,
+                                    { 'hostname' : hostname },
+                                    [ 'mac','method' ])
+
+    if len(nnlist) != 1:
+        return []
+
+    (host,network) = hostname.split('.',1)
+
+    return [ 'NODE_ID="%d"' % nlist[0]['node_id'],
+             'NODE_KEY="%s"' % nlist[0]['key'],
+             'NET_DEVICE="%s"' % nnlist[0]['mac'],
+             'IP_METHOD="%s"' % nnlist[0]['method'],
+             'HOST_NAME="%s"' % host,
+             'DOMAIN_NAME="%s"' % network ]
+
+
+def readRootAcctInfo(refresh=False,pwfile=DEF_ROOT_ACCOUNT_INFO_FILE):
+    global cachedRootUID,cachedRootPasswd
+
+    if not os.path.isfile(pwfile):
+        return None
+
+    if not refresh and (cachedRootUID != None and cachedRootPasswd != None):
+        return (cachedRootUID,cachedRootPasswd)
+    
+    info = list()
+    try:
+        fd = open(pwfile,'r')
+        line = fd.readline().strip('\n')
+        while line != '':
+            if not line.startswith('#'):
+                info.append(line)
+                pass
+            line = fd.readline().strip('\n')
+            pass
+        pass
+    except:
+        if debug:
+            print "Failed to read root account info from %s" % pwfile
+            traceback.print_exc()
+            pass
+        raise
+
+    if info == []:
+        return None
+
+    (cachedRootUID,cachedRootPasswd) = tuple(info)
+    
+    return tuple(info)
+
+
+def writeRootAcctInfo(uid=DEF_ROOT_UID,passwd=None,
+                      pwfile=DEF_ROOT_ACCOUNT_INFO_FILE):
+    global cachedRootUID,cachedRootPasswd
+    
+    tpass = passwd
+    if tpass == None:
+        astr = "abcdefghijklmnopqrstuvwxyz"
+        rstr = astr + astr.upper() + '0123456789' + '!@#$%^&*'
+        
+        tpasslist = random.sample(rstr,8)
+        tpass = ''
+        for char in tpasslist:
+            tpass += char
+            pass
+        pass
+
+    try:
+        fd = open(pwfile,'w')
+        fd.write("%s\n" % uid)
+        fd.write("%s\n" % tpass)
+        fd.close()
+    except:
+        if debug:
+            print "Failed to write root account info to %s" % pwfile
+            traceback.print_exc()
+            pass
+        raise
+
+    cachedRootUID = uid
+    cachedRootPasswd = tpass
+
+    return (cachedRootUID,cachedRootPasswd)
+
+
+
diff --git a/tmcd/myplc/plcsetup.py b/tmcd/myplc/plcsetup.py
new file mode 100755
index 0000000000000000000000000000000000000000..6e2dc09617b8a145b374e914b3559b3ecc085632
--- /dev/null
+++ b/tmcd/myplc/plcsetup.py
@@ -0,0 +1,392 @@
+#!/usr/bin/python
+
+import traceback
+import sys
+import os.path
+
+#
+# Helper functions.
+#
+def runCommand(cmd):
+    output = list()
+    cp = os.popen(cmd,'r')
+    line = cp.readline()
+    while line != '':
+        output.append(line)
+        line = cp.readline()
+        pass
+    return output
+
+def getHostname():
+    return runCommand('/bin/hostname')[0].strip('\n')
+
+def doService(serviceName,serviceAction):
+    #print "Sending %s to %s" % (serviceAction,serviceName)
+    return os.system("service %s %s" % (serviceName,serviceAction))
+
+def readUserKeys(uid):
+    """
+    Reads the Emulab-generated /users/@uid@/.ssh/authorized_keys file
+    and returns a list of the keys contained therein.
+    """
+    keyfile = '/users/%s/.ssh/authorized_keys' % uid
+    fd = file(keyfile)
+    keylist = list()
+
+    for line in fd:
+        if line != '' and not line.startswith('#'):
+            keylist.append(line.strip('\n'))
+            pass
+        pass
+
+    return keylist
+
+def runTMCC(cmd):
+    """
+    Runs the tmcc command indicated and returns, for each line, a list of
+    keys with no value, and a dict of key/value pairs.
+    """
+    retlist = list()
+    retval = runCommand("%s %s" % (TMCC,cmd))
+    
+    for line in retval:
+        rline = line.strip('\n')
+        lineDict = dict()
+        lineDict['onlykey'] = []
+
+        while len(rline) > 0:
+            [k,rline] = rline.split('=',1)
+            if k.count(' ') > 0:
+                # "key" is really multiple keys...
+                realkeys = k.split(' ')
+                k = realkeys[-1]
+                for r in realkeys[0:-1]:
+                    lineDict['onlykey'].append(r)
+                    pass
+                pass
+            if rline[0] == '"':
+                # need to split at the next '" '
+                [v,rline] = rline[1:].split('"',1)
+                if len(rline) > 0:
+                    # get rid of the next space...
+                    rline = rline[1:]
+                    pass
+                pass
+            else:
+                if rline.count(' ') > 0:
+                    [v,rline] = rline.split(' ',1)
+                    pass
+                else:
+                    v = rline
+                    rline = ''
+                    pass
+                pass
+            lineDict[k] = v
+            pass
+        
+        retlist.append(lineDict)
+        pass
+
+    return retlist
+
+# -4. Must be root.
+# -3. Stop and mount plc.
+# -2. Create some dirs we need.
+# -1. Read in necessary Emulab tmcc data.
+#  0. Read in (set if can't read) plc root account info.
+#  1. Read in plc config, reset any of the key defaults that are incorrect.
+#  2. Restart plc so we can talk to xmlrpc server and so changes are grabbed.
+#  3. Do a diff on the current plc nodes/networks/users and add/remove as
+#     necessary.
+#  4. Extract crap from the bootcd, make config imgs, setup pxe configs.
+#  5. Stop plc from running on boot, since this setup will run it.
+
+
+# -4.
+if not os.getuid() == 0:
+    print "ERROR: must be root to run this script!"
+    sys.exit(-1)
+    pass
+
+# -3.
+print "plabinelab: stopping plc:"
+doService('plc','stop')
+print "plabinelab: mounting plc:"
+doService('plc','mount')
+
+# ick!  need to patch the bootmanager so that 1) it doesn't ask if we're sure
+# before installing, and 2) it doesn't fail the hardware check on slow nodes.
+if not os.path.exists('/plc/emulab/bootmanager.patch.done'):
+    print "plabinelab: patching bootmanager"
+    cwd = os.getcwd()
+    os.chdir('/plc/root/usr/share/bootmanager')
+    os.system('patch -p0 < /plc/emulab/bootmanager.patch')
+    os.chdir(cwd)
+    bpd = open('/plc/emulab/bootmanager.patch.done','w')
+    bpd.write('done\n')
+    bpd.close()
+    pass
+
+# can only import this after plc is mounted; else we can't get access to the
+# python modules.
+from libplcsetup import *
+
+TMCC = '/usr/local/etc/emulab/tmcc'
+# XXX: need to switch this stuff to be a little more intelligent so
+# we can configure private planetlab networks from the control net.
+DEF_PLC_HOST = getHostname()
+DEF_PLC_IP = socket.gethostbyname(DEF_PLC_HOST)
+
+# -2.
+#try:
+#    os.makedirs('/plc/emulab/setup')
+#except:
+#    pass
+try:
+    os.makedirs('/plc/emulab/nodes')
+except:
+    pass
+
+# -1.
+print "plabinelab: gathering info from tmcd"
+tmccAccounts = runTMCC('accounts')
+tmccCreator = runTMCC('creator')
+tmccEPlabConfig = runTMCC('eplabconfig')
+
+(creatorUID,creatorEmail) = (None,None)
+for acct in tmccAccounts:
+    if 'ADDUSER' in acct['onlykey'] \
+       and acct['LOGIN'] == tmccCreator[0]['CREATOR']:
+        # found creator
+        (creatorUID,creatorEmail) = (acct['LOGIN'],acct['EMAIL'])
+        break
+    pass
+if creatorUID == None or creatorEmail == None:
+    print "ERROR: could not find experiment creator's user info!"
+    sys.exit(-1)
+    pass
+
+
+# 0.
+(rootUID,rootPasswd) = (None,None)
+try:
+    ret = readRootAcctInfo()
+    if ret != None and len(ret) == 2:
+        (rootUID,rootPasswd) = ret
+        print "plabinelab: read root account info"
+        pass
+except:
+    print "ERROR: could not read root account info!"
+    traceback.print_exc()
+    sys.exit(-1)
+    pass
+
+if rootUID == None or rootPasswd == None:
+    try:
+        (rootUID,rootPasswd) = writeRootAcctInfo()
+        print "plabinelab: wrote new root account info"
+    except:
+        print "ERROR: could not write root account info!"
+        traceback.print_exc()
+        sys.exit(-1)
+        pass
+    pass
+
+# 1.
+# XXX: can eventually store these in some other xml file, then merge them in
+# with the main plc config file.  Better to do it this way since then
+# plc-config-tty will still work...
+
+# grab the PLC's name while we're at it...
+PLC_NAME = plcReadConfigVar('plc','name')
+
+print "plabinelab: updating config for PLC '%s'" % str(PLC_NAME)
+configVarsList = [ [ 'plc_www','ip',              DEF_PLC_IP ],
+                   [ 'plc_www','host',            DEF_PLC_HOST ],
+                   [ 'plc','root_user',           rootUID ],
+                   [ 'plc','root_password',       rootPasswd ],
+                   [ 'plc_boot','ip',             DEF_PLC_IP ],
+                   [ 'plc_boot','host',           DEF_PLC_HOST ],
+                   [ 'plc_mail','support_address',creatorEmail ],
+                   [ 'plc_mail','boot_address',   creatorEmail ],
+                   [ 'plc_mail','slice_address',  creatorEmail ],
+                   [ 'plc_api','ip',              DEF_PLC_IP ],
+                   [ 'plc_api','host',            DEF_PLC_HOST ],
+                   [ 'plc_db','ip',               DEF_PLC_IP ],
+                   [ 'plc_db','host',             DEF_PLC_HOST ] ]
+plcUpdateConfig(configVarsList)
+
+# XXX: can't find a good way to grab this, but it's unlikely that it will
+# change during emulab exp runtime.
+PLC_BOOTCD_VERSION = '3.3'
+
+# 2.
+print "plabinelab: restarting plc"
+doService('plc','start')
+
+# 3.
+# first update users:
+print "plabinelab: updating user accounts"
+userlist = list()
+for lineDict in tmccAccounts:
+    if 'ADDUSER' in lineDict['onlykey']:
+        root = False
+        if lineDict['ROOT'] == '1':
+            root = True
+            pass
+        # we use the @emulab.net address because it makes it easier for us
+        # to later remove users during a swapmod (there are legit plc users
+        # in the db that are needed for plc maint).
+        userlist.append((lineDict['NAME'],
+                         lineDict['LOGIN'] + '@emulab.net',lineDict['PSWD'],
+                         readUserKeys(lineDict['LOGIN']),root))
+        pass
+    pass
+
+plcUpdateUsers(userlist)
+
+# now do nodes:
+print "plabinelab: updating nodes"
+nodelist = list()
+for lineDict in tmccEPlabConfig:
+    if lineDict.has_key('ROLE') and lineDict['ROLE'] == 'node':
+        nodelist.append((lineDict['PNAME'],
+                         lineDict['CNETIP'],lineDict['CNETMAC']))
+        pass
+    pass
+
+plcUpdateNodes(nodelist)
+
+# 4.
+# first create the local node config info:
+vnameToNID = dict()
+nidToMAC = dict()
+# XXX: this does depend on tmcd returning the ROLE lines before the private
+# iface lines for each vname.
+for lineDict in tmccEPlabConfig:
+    if lineDict.has_key('ROLE') and lineDict['ROLE'] == 'node':
+        nid = plcGetNodeID(lineDict['PNAME'])
+
+        print "plabinelab: generating config files for node id %d" % nid
+
+        vnameToNID[lineDict['VNAME']] = nid
+        nidToMAC[nid] = lineDict['CNETMAC']
+        
+        configLines = plcGetNodeConfig(lineDict['PNAME'])
+        macLines = [ addMACDelim(lineDict['CNETMAC'],'-') ]
+
+        if not os.path.exists('/plc/emulab/nodes/%d' % nid):
+            os.makedirs('/plc/emulab/nodes/%d' % nid)
+            pass
+
+        cfd = open('/plc/emulab/nodes/%d/conf' % nid,'w')
+        for cl in configLines:
+            cfd.write('%s\n' % cl)
+            pass
+        cfd.close()
+
+        mfd = open('/plc/emulab/nodes/%d/mac' % nid,'w')
+        for ml in macLines:
+            mfd.write('%s\n' % ml)
+            pass
+        mfd.close()
+
+        pass
+    elif not lineDict.has_key('ROLE') and lineDict.has_key('VNAME'):
+        nid = vnameToNID[lineDict['VNAME']]
+
+        pfd = open('/plc/emulab/nodes/%d/ifcfg-eth1' % nid,'w')
+        pfd.write("DEVICE=eth1\n")
+        pfd.write("BOOTPROTO=none\n")
+        pfd.write("IPADDR=%s\n" % lineDict['IP'])
+        pfd.write("NETMASK=%s\n" % lineDict['NETMASK'])
+        pfd.write("HWADDR=%s\n" % addMACDelim(lineDict['MAC'],':'))
+        pfd.write("ONBOOT=yes\n")
+        pfd.write("TYPE=Ethernet\n")
+        pfd.close()
+        pass
+    pass
+
+# tar it up...
+for nid in vnameToNID.values():
+    print "plabinelab: creating config tarball for node id %d" % nid
+    os.system('rm -rf /tmp/ncfg-root')
+    os.makedirs('/tmp/ncfg-root/etc')
+    os.system('cp /etc/hosts /tmp/ncfg-root/etc/')
+    if os.path.isfile('/plc/emulab/nodes/%d/ifcfg-eth1' % nid):
+        os.makedirs('/tmp/ncfg-root/etc/sysconfig/network-scripts')
+        os.system('cp /plc/emulab/nodes/%d/ifcfg-eth1 /tmp/ncfg-root/etc/sysconfig/network-scripts' % nid)
+        pass
+    cwd = os.getcwd()
+    os.chdir('/tmp/ncfg-root')
+    os.system('tar cf /plc/data/var/www/html/download/%d.tar .' % nid)
+    os.chdir(cwd)
+    os.system('rm -rf /tmp/ncfg-root')
+    pass
+
+# setup tftp:
+# we have to extract the node kernel that chainboots into the real kernel,
+# the various img files, and create an img with config data for each node.
+# also end up adjusting the isolinux boot cmdline.
+
+print "plabinelab: extracting info from BootCD"
+
+if not os.path.exists('/mnt/bootcd'):
+    os.makedirs('/mnt/bootcd')
+    pass
+os.system('mount -o loop "/plc/data/var/www/html/download/' \
+          '%s-BootCD-%s-serial.iso" /mnt/bootcd' % (PLC_NAME,
+                                                    PLC_BOOTCD_VERSION))
+os.system('cp /mnt/bootcd/pl_version /tftpboot')
+os.system('cp /mnt/bootcd/kernel /tftpboot')
+os.system('cp /mnt/bootcd/*.img /tftpboot')
+
+ilfd = open('/mnt/bootcd/isolinux.cfg','r')
+ilconfigLines = ilfd.read().split('\n')
+ilfd.close()
+
+for nid in vnameToNID.values():
+    print "plabinelab: configuring pxelinux for node id %d" % nid
+    
+    os.system('rm -rf /tmp/real-ncfg')
+    os.makedirs('/tmp/real-ncfg/usr/boot')
+    os.system('cp /plc/emulab/nodes/%d/conf ' \
+              '/tmp/real-ncfg/usr/boot/plnode.txt' % nid)
+    cwd = os.getcwd()
+    os.chdir('/tmp/real-ncfg')
+    os.system('find . | cpio -o -c | gzip -9 > /tftpboot/config-%d.img' % nid)
+    os.system('rm -rf /tmp/real-ncfg')
+    os.chdir(cwd)
+
+    if not os.path.exists('/tftpboot/pxelinux.cfg'):
+        os.makedirs('/tftpboot/pxelinux.cfg')
+        pass
+
+    pfd = open('/tftpboot/pxelinux.cfg/01-%s' % addMACDelim(nidToMAC[nid],'-'),'w')
+    for iline in ilconfigLines:
+        rline = ''
+        if iline.count('initrd') > 0:
+            sline = iline.split('img')
+            for sl in sline[:-1]:
+                rline += sl + 'img'
+                pass
+            rline += ',config-%d.img' % nid
+            rline += sline[-1]
+            pass
+        else:
+            rline = iline
+            pass
+        pfd.write('%s\n' % rline)
+        pass
+    pfd.close()
+    pass
+
+os.system('umount /mnt/bootcd')
+
+# 5.
+os.system('chkconfig plc off')
+
+# Finis.
+print "plabinelab: done!"
+
+sys.exit(0)