starting new development to support slice conf files
authorTony Mack <tmack@cs.princeton.edu>
Fri, 10 Oct 2008 19:19:26 +0000 (19:19 +0000)
committerTony Mack <tmack@cs.princeton.edu>
Fri, 10 Oct 2008 19:19:26 +0000 (19:19 +0000)
tools/dzombie.py [new file with mode: 0755]
tools/planetlab3_dump.sh [new file with mode: 0755]
tools/plcdb.3-4.conf [new file with mode: 0644]
tools/slice_attributes.py [new file with mode: 0755]
tools/upgrade-db.py [new file with mode: 0755]

diff --git a/tools/dzombie.py b/tools/dzombie.py
new file mode 100755 (executable)
index 0000000..e3c170a
--- /dev/null
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+#
+# Tool that removes zombie records from database tables#
+import sys
+import os
+import getopt
+import pgdb
+from pprint import pprint
+
+schema_file = None
+config_file = "/etc/planetlab/plc_config"
+config = {}
+execfile(config_file, config)
+
+def usage():
+        print "Usage: %s SCHEMA_FILE " % sys.argv[0]
+        sys.exit(1)
+
+try:
+               schema_file  = sys.argv[1]
+except IndexError:
+        print "Error: too few arguments"
+        usage()
+
+# all foreing keys exist as primary kyes in another table
+# will represent all foreign keys as
+# { 'table.foreign_key': 'table.primary_key'} 
+foreign_keys = {}
+foreign_keys_ordered = []
+zombie_keys = {}
+# parse the schema for foreign keys
+try:
+        file = open(schema_file, 'r')
+        index = 0
+        lines = file.readlines()
+        while index < len(lines):
+               line = lines[index].strip()
+                # find all created objects
+                if line.startswith("CREATE"):
+                       line_parts = line.split(" ")
+                       if line_parts[1:3] == ['OR', 'REPLACE']:
+                               line_parts = line_parts[2:]
+                       item_type = line_parts[1].strip()
+                       item_name = line_parts[2].strip()
+                       if item_type.upper() in ['TABLE']:
+                               while index < len(lines):
+                                       index = index + 1
+                                       nextline =lines[index].strip()
+                                       if nextline.find("--") > -1:
+                                               nextline = nextline[0:nextline.index("--")].replace(',', '')
+                                       if nextline.upper().find("REFERENCES") > -1:
+                                               nextline_parts = nextline.split(" ")
+                                               foreign_key_name = nextline_parts[0].strip()
+                                               foreign_key_table = nextline_parts[nextline_parts.index("REFERENCES")+1].strip()
+                                               foreign_key = item_name + "."+ foreign_key_name
+                                               primary_key = foreign_key_table +"."+ foreign_key_name 
+                                               foreign_keys[foreign_key] = primary_key
+                                               foreign_keys_ordered.append(foreign_key)
+                                       elif nextline.find(";") >= 0:
+                                                break
+               index = index + 1
+except:
+       raise
+
+db = pgdb.connect(user = config['PLC_DB_USER'],
+                  database = config['PLC_DB_NAME'])
+cursor = db.cursor()
+try:
+       for foreign_key in foreign_keys_ordered:
+               primary_key = foreign_keys[foreign_key]
+               sql = "SELECT distinct %s from %s"
+               
+               # get all foreign keys in this table
+               foreign_key_parts = foreign_key.split(".")
+       
+               # do not delete from primary tables
+               if foreign_key_parts[0] in ['addresses', 'boot_states', 'conf_files', \
+                       'keys', 'messages', 'nodegroups', 'nodenetworks', 'nodes', 'pcus', 'peers' \
+                        'persons', 'roles', 'sessions', 'sites', 'slices']:
+                       #print "skipping table %s" % foreign_key_parts[0] 
+                       continue
+
+               cursor.execute(sql % (foreign_key_parts[1], foreign_key_parts[0]))
+               foreign_rows = cursor.fetchall()
+                               
+               # get all the primary keys from this foreign key's primary table 
+               primary_key_parts = primary_key.split(".")
+               # foreign key name may not match primary key name. must rename these
+               if primary_key_parts[1] == 'creator_person_id':
+                       primary_key_parts[1] = 'person_id'
+               elif primary_key_parts[1] == 'min_role_id':
+                       primary_key_parts[1]  = 'role_id'
+               sql = sql % (primary_key_parts[1], primary_key_parts[0])
+               
+               # determin which primary records are deleted
+               desc = os.popen('psql planetlab4 postgres -c "\d %s;"' % primary_key_parts[0])
+                result = desc.readlines()
+               if primary_key_parts[0] in ['slices']:
+                       sql  = sql + " where name not like '%_deleted'"
+               elif filter(lambda line: line.find("deleted") > -1, result):
+                       sql = sql + " where deleted = false"
+
+               cursor.execute(sql)
+               primary_key_rows = cursor.fetchall()
+               
+               # if foreign key isnt present in primay_key query, it either doesnt exist or marked as deleted
+               # also, ignore null foreign keys, not considered zombied
+               zombie_keys_func = lambda key: key not in primary_key_rows and not key == [None]
+               zombie_keys_list = [zombie_key[0] for zombie_key in filter(zombie_keys_func, foreign_rows)]
+               print zombie_keys_list
+               # delete these zombie records
+               if zombie_keys_list:
+                       print " -> Deleting %d zombie record(s) from %s after checking %s" % \
+                                       (len(zombie_keys_list), foreign_key_parts[0], primary_key_parts[0])
+                       sql_delete = 'DELETE FROM %s WHERE %s IN %s' % \
+                       (foreign_key_parts[0], foreign_key_parts[1], tuple(zombie_keys_list))
+                       cursor.execute(sql_delete)
+                       db.commit()
+               #zombie_keys[foreign_key] = zombie_keys_list
+       print "done"
+except pgdb.DatabaseError:
+       raise
diff --git a/tools/planetlab3_dump.sh b/tools/planetlab3_dump.sh
new file mode 100755 (executable)
index 0000000..73f6b86
--- /dev/null
@@ -0,0 +1,119 @@
+#!/bin/bash
+#
+# Dumps the planetlab3 database on zulu, fixing a few things on the way
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2007 The Trustees of Princeton University
+#
+# $Id: planetlab3_dump.sh 5574 2007-10-25 20:33:17Z thierry $
+#
+
+tables=(
+node_bootstates
+nodes
+nodenetworks
+node_nodenetworks
+nodegroups
+nodegroup_nodes
+override_bootscripts
+pod_hash
+conf_file
+conf_assoc
+address_types
+addresses
+organizations
+sites
+roles
+capabilities
+persons
+person_roles
+person_capabilities
+person_address
+key_types
+keys
+person_keys
+person_site
+node_root_access
+authorized_subnets
+site_authorized_subnets
+event_classes
+dslice03_states
+dslice03_attributetypes
+dslice03_slices
+dslice03_attributes
+dslice03_sliceattribute
+dslice03_slicenode
+dslice03_sliceuser
+dslice03_siteinfo
+pcu
+pcu_ports
+join_request
+whatsnew
+node_hostnames
+blacklist
+dslice03_initscripts
+dslice03_defaultattribute
+peered_mas
+sessions
+)
+
+# Dump tables
+for table in "${tables[@]}" ; do
+    pg_dump -U postgres -t $table planetlab3
+done |
+
+# Do some manual cleanup
+sed -f <(cat <<EOF
+# Swap person_id=1 (kfall@cs.berkeley.edu) with person_id=1303 (maint@planet-lab.org)
+/^COPY \(persons\|person_roles\|person_capabilities\|person_address\|person_keys\|person_site\|node_root_access\)/,/^\\\./{
+s/^1\t/1303\t/
+t
+s/^1303\t/1\t/
+t
+}
+/^COPY dslice03_sliceuser/,/^\\\./{
+s/\t1$/\t1303/
+t
+s/\t1303$/\t1/
+t
+}
+
+# Swap person_id=2 (nakao@cs.princeton.edu) with person_id=13342 (root@planet-lab.org)
+/^COPY \(persons\|person_roles\|person_capabilities\|person_address\|person_keys\|person_site\|node_root_access\)/,/^\\\./{
+s/^2\t/13342\t/
+t
+s/^13342\t/2\t/
+t
+}
+/^COPY dslice03_sliceuser/,/^\\\./{
+s/\t2$/\t13342/
+t
+s/\t13342$/\t2/
+t
+}
+
+# Swap site_id=1 (gt) with site_id=90 (pl)
+/^COPY \(sites\|site_authorized_subnets\|dslice03_siteinfo\)/,/^\\\./{
+s/^1\t/90\t/
+t
+s/^90\t/1\t/
+t
+}
+/^COPY \(person_site\|pcu\)/,/^\\\./{
+s/\([^\t]*\t\)1\t/\190\t/
+t
+s/\([^\t]*\t\)90\t/\11\t/
+t
+}
+/^COPY \(dslice03_slices\)/,/^\\\./{
+s/\([^\t]*\t[^\t]*\t\)1\t/\190\t/
+t
+s/\([^\t]*\t[^\t]*\t\)90\t/\11\t/
+t
+}
+EOF
+)
+
+# Dump events and api_log schema only
+pg_dump -U postgres -s -t events planetlab3
+pg_dump -U postgres -s -t api_log planetlab3
diff --git a/tools/plcdb.3-4.conf b/tools/plcdb.3-4.conf
new file mode 100644 (file)
index 0000000..9620703
--- /dev/null
@@ -0,0 +1,62 @@
+# configuration file that describes the differences
+
+# new_table_name = field:old_table.field[:required_join_table.join_using:...], ...
+
+DB_VERSION_PREVIOUS = '3'
+
+DB_VERSION_NEW = '4'
+
+sites = 'site_id:sites.site_id, login_base:sites.login_base, name:sites.name, abbreviated_name:sites.abbreviated_name, deleted:sites.deleted, is_public:sites.is_public, max_slices:dslice03_siteinfo.max_slices:dslice03_siteinfo.site_id, latitude:sites.latitude, longitude:sites.longitude, url:sites.url, date_created:sites.date_created'
+
+persons = 'person_id:persons.person_id, email:persons.email, first_name:persons.first_name, last_name:persons.last_name, deleted:persons.deleted, enabled:persons.enabled, password:persons.password, verification_key:persons.verification_key, verification_expires:persons.verification_expires, title:persons.title, phone:persons.phone, url:persons.url, bio:persons.bio'
+
+person_site = 'person_id:person_site.person_id, site_id:person_site.site_id, is_primary:person_site.is_primary'
+
+address_types = 'address_type_id:address_types.address_type_id, name:address_types.name'
+
+addresses = 'address_id:addresses.address_id:addresses.address_type_id=10001, line1:addresses.line1, line2:addresses.line2, line3:addresses.line3, city:addresses.city, state:addresses.state, postalcode:addresses.postalcode, country:addresses.country'
+
+site_address = 'address_id:addresses.address_id, site_id:person_site.site_id:person_address.address_id:person_site.person_id:addresses.address_type_id=10001'
+
+address_address_type = 'address_id:addresses.address_id:addresses.address_type_id=10001, address_type_id:addresses.address_type_id'
+
+key_types = 'key_type:key_types.key_type'
+
+keys = 'key_id:keys.key_id:person_keys.key_id:person_keys.deleted=false, key_type:keys.key_type, key:keys.key, is_blacklisted:keys.is_blacklisted'
+
+person_key = 'person_id:person_keys.person_id:person_keys.deleted=false, key_id:person_keys.key_id'
+
+roles = 'role_id:roles.role_id, name:roles.name'
+
+person_role = 'person_id:person_roles.person_id, role_id:person_roles.role_id'
+
+boot_states = 'boot_state:node_bootstates.boot_state'
+
+nodes = 'node_id:nodes.node_id, hostname:nodes.hostname, site_id:sites.site_id:nodegroup_nodes.node_id:sites.nodegroup_id, boot_state:nodes.boot_state , deleted:nodes.deleted , model:nodes.model , boot_nonce:nodes.boot_nonce, version:nodes.version, ssh_rsa_key:nodes.ssh_rsa_key, key:nodes.key, date_created:nodes.date_created'
+
+nodegroups = 'nodegroup_id:nodegroups.nodegroup_id, name:nodegroups.name, description:nodegroups.description'
+
+nodegroup_node = 'nodegroup_id:nodegroup_nodes.nodegroup_id, node_id:nodegroup_nodes.node_id'
+
+# conf_files = 'conf_file_id:conf_file.conf_file_id, enabled:conf_file.enabled, source:conf_file.source, dest:conf_file.dest, file_permissions:conf_file.file_permissions, file_owner:conf_file.file_owner, file_group:conf_file.file_group, preinstall_cmd:conf_file.preinstall_cmd, postinstall_cmd:conf_file.postinstall_cmd, error_cmd:conf_file.error_cmd, ignore_cmd_errors:conf_file.ignore_cmd_errors, always_update:conf_file.always_update'
+
+# conf_file_node = 'conf_file_id:conf_assoc.conf_file_id, node_id:conf_assoc.node_id'
+
+# conf_file_nodegroup = 'conf_file_id:conf_assoc.conf_file_id, nodegroup_id:conf_assoc.nodegroup_id'
+
+nodenetworks = 'nodenetwork_id:nodenetworks.nodenetwork_id, node_id:node_nodenetworks.node_id:node_nodenetworks.nodenetwork_id, is_primary:node_nodenetworks.is_primary:node_nodenetworks.nodenetwork_id, type:nodenetworks.type, method:nodenetworks.method, ip:nodenetworks.ip, mac:nodenetworks.mac, gateway:nodenetworks.gateway, network:nodenetworks.network, broadcast:nodenetworks.broadcast, netmask:nodenetworks.netmask, dns1:nodenetworks.dns1, dns2:nodenetworks.dns2, bwlimit:nodenetworks.bwlimit, hostname:nodenetworks.hostname'
+
+pcus = 'pcu_id:pcu.pcu_id, site_id:pcu.site_id, hostname:pcu.hostname, ip:pcu.ip, protocol:pcu.protocol, username:pcu.username, password:pcu.password, model:pcu.model, notes:pcu.notes'
+
+pcu_node = 'pcu_id:pcu_ports.pcu_id, node_id:pcu_ports.node_id, port:pcu_ports.port_number'
+
+slices = 'slice_id:dslice03_slices.slice_id, site_id:dslice03_slices.site_id, name:dslice03_slices.name, instantiation:dslice03_states.name:dslice03_states.state_id, url:dslice03_slices.url, description:dslice03_slices.description, max_nodes:dslice03_siteinfo.max_slices:dslice03_siteinfo.site_id, creator_person_id:dslice03_slices.creator_person_id, created:dslice03_slices.created, expires:dslice03_slices.expires, is_deleted:dslice03_slices.is_deleted'
+
+slice_node = 'slice_id:dslice03_slicenode.slice_id, node_id:dslice03_slicenode.node_id'
+
+slice_person = 'slice_id:dslice03_sliceuser.slice_id, person_id:dslice03_sliceuser.person_id'
+
+slice_attribute_types = 'attribute_type_id:dslice03_attributetypes.type_id, name:dslice03_attributetypes.name, description:dslice03_attributetypes.description, min_role_id:dslice03_attributetypes.min_role_id'
+
+slice_attribute = 'slice_attribute_id:dslice03_sliceattribute.attribute_id, slice_id:dslice03_sliceattribute.slice_id, attribute_type_id:dslice03_attributes.type_id:dslice03_attributes.attribute_id, value:dslice03_attributes.value1:dslice03_attributes.attribute_id'
+
diff --git a/tools/slice_attributes.py b/tools/slice_attributes.py
new file mode 100755 (executable)
index 0000000..502c22b
--- /dev/null
@@ -0,0 +1,282 @@
+#!/usr/bin/env /usr/bin/plcsh
+#
+# Convert old planetlab3 slice attributes and initscripts to new
+# planetlab4 ones.
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+# $Id: slice_attributes.py 5574 2007-10-25 20:33:17Z thierry $
+#
+
+import re
+import base64
+
+# Convert nm_net_{exempt_,}{min,max}_rate (bps) to
+# net_{i2_,}{min,max}_rate and net_{i2_,}{min,max}_rate (kbps)
+rename = {'nm_net_min_rate': 'net_min_rate',
+          'nm_net_max_rate': 'net_max_rate',
+          'nm_net_exempt_min_rate': 'net_i2_min_rate',
+          'nm_net_exempt_max_rate': 'net_i2_max_rate'}
+for slice_attribute in GetSliceAttributes({'name': rename.keys()}):
+    id = slice_attribute['slice_attribute_id']
+    name = slice_attribute['name']
+    slice_id = slice_attribute['slice_id']
+
+    # Convert bps to kbps
+    bps = int(slice_attribute['value'])
+    kbps = bps / 1000
+
+    # Add the new attribute
+    if GetSlices([slice_id]):
+        AddSliceAttribute(slice_id, rename[name], str(kbps))
+
+    # Delete the old attribute
+    DeleteSliceAttribute(id)
+
+# Convert nm_net_{exempt_,}avg_rate to
+# net_{i2_,}max_kbyte and net_{i2_,}thresh_kbyte
+rename = {'nm_net_avg_rate': {'max': 'net_max_kbyte',
+                              'thresh': 'net_thresh_kbyte'},
+          'nm_net_exempt_avg_rate': {'max': 'net_i2_max_kbyte',
+                                     'thresh': 'net_i2_thresh_kbyte'}}
+for slice_attribute in GetSliceAttributes({'name': rename.keys()}):
+    id = slice_attribute['slice_attribute_id']
+    name = slice_attribute['name']
+    slice_id = slice_attribute['slice_id']
+
+    # Convert bps to 80% and 100% of max bytes per day
+    bps = int(slice_attribute['value'])
+    max_kbyte = bps * 24 * 60 * 60 / 8 / 1000
+    thresh_kbyte = int(0.8 * max_kbyte)
+
+    # Add the new attribute
+    if GetSlices([slice_id]):
+        AddSliceAttribute(slice_id, rename[name]['max'], str(max_kbyte))
+        AddSliceAttribute(slice_id, rename[name]['thresh'], str(thresh_kbyte))
+
+    # Delete the old attribute
+    DeleteSliceAttribute(id)
+
+# Convert plc_slice_state
+for slice_attribute in GetSliceAttributes({'name': 'plc_slice_state'}):
+    id = slice_attribute['slice_attribute_id']
+    name = slice_attribute['name']
+    slice_id = slice_attribute['slice_id']
+
+    # Add the new attribute
+    if GetSlices([slice_id]):
+        if slice_attribute['value'] == "suspended":
+            AddSliceAttribute(slice_id, 'enabled', "0")
+        else:
+            AddSliceAttribute(slice_id, 'enabled', "1")
+
+    # Delete the old attribute
+    DeleteSliceAttribute(id)
+    
+# Straight renames
+rename = {'nm_cpu_share': 'cpu_share',
+          'nm_disk_quota': 'disk_max',
+          'nm_net_share': 'net_share',
+          'nm_net_exempt_share': 'net_i2_share',
+          'nm_net_max_byte': 'net_max_kbyte',
+          'nm_net_max_thresh_byte': 'net_thresh_kbyte',
+          'nm_net_max_exempt_byte': 'net_i2_max_kbyte',
+          'nm_net_max_thresh_exempt_byte': 'net_i2_thresh_kbyte'}
+for slice_attribute in GetSliceAttributes({'name': rename.keys()}):
+    id = slice_attribute['slice_attribute_id']
+    name = slice_attribute['name']
+    slice_id = slice_attribute['slice_id']
+
+    # Pass straight through
+    value = slice_attribute['value']
+
+    # Add the new attribute
+    if GetSlices([slice_id]):
+        AddSliceAttribute(slice_id, rename[name], value)
+
+    # Delete the old attribute
+    DeleteSliceAttribute(id)
+
+# Update plc_ticket_pubkey attribute
+for slice_attribute in GetSliceAttributes({'name': "plc_ticket_pubkey"}):
+    id = slice_attribute['slice_attribute_id']
+
+    UpdateSliceAttribute(id, """
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKXa72MEKDAnVyzEpKOB1ot2eW
+xG/TG2aa7q/2oy1xf5XMmU9H9uKwO+GoUeinp1BSxgkVRF0VhEGGaqKR9kYQzX0k
+ht4+P2hAr+UyU4cp0NxV4xfmyAbrNKuHVjawMUCu5BH0IkBUC/89ckxk71oROnak
+FbI7ojUezSGr4aVabQIDAQAB
+""".lstrip())
+
+# Delete _deleted and deprecated slice attributes and types
+for attribute_type in GetSliceAttributeTypes():
+    id = attribute_type['attribute_type_id']
+    name = attribute_type['name']
+
+    if name == 'general_prop_share' or \
+       re.match('nm_', name) or \
+       re.search('_deleted$', name):
+        DeleteSliceAttributeType(id)
+        # N.B. Automatically deletes all slice attributes of this type
+
+# Add Proper ops
+proper_ops = [
+    # give Stork permission to mount and unmount client dirs
+    ('arizona_stork', 'mount_dir'),
+    ('arizona_stork', 'set_file_flags pass, "1"'),
+    ('arizona_stork', 'set_file_flags_list "1"'),
+    ('arizona_stork', 'bind_socket sockname=64?:*'),
+    ('arizona_stork2', 'mount_dir'),
+    ('arizona_stork2', 'set_file_flags pass, "1"'),
+    ('arizona_stork2', 'set_file_flags_list "1"'),
+    ('arizona_stork2', 'bind_socket sockname=64?:*'),
+
+    # give CoMon the necessary permissions to run slicestat
+    ('princeton_slicestat', 'exec "root", pass, "/usr/local/planetlab/bin/pl-ps", none'),
+    ('princeton_slicestat', 'exec "root", pass, "/usr/sbin/vtop", "bn1", none'),
+    ('princeton_slicestat', 'open_file file=/proc/virtual/*/cacct'),
+    ('princeton_slicestat', 'open_file file=/proc/virtual/*/limit'),
+    ('princeton_comon', 'open_file file=/var/log/secure'),
+    ('princeton_comon', 'exec "root", pass, "/bin/df", "/vservers", none'),
+
+    # give pl_slicedir access to /etc/passwd
+    ('pl_slicedir', 'open_file pass, "/etc/passwd"'),
+
+    # nyu_d are building a DNS demux so give them access to port 53
+    ('nyu_d', 'bind_socket'),
+    ('nyu_oasis', 'bind_socket'),
+
+    # QA slices need to be able to create and delete bind-mounts
+    ('pl_qa_0', 'mount_dir'),
+    ('pl_qa_1', 'mount_dir'),
+
+    # irb_snort needs packet sockets for tcpdump
+    ('irb_snort', 'create_socket'),
+
+    # uw_ankur is using netlink sockets to do the same thing as netflow
+    ('uw_ankur', 'create_socket'),
+
+    # cornell_codons gets access to port 53 for now
+    ('cornell_codons', 'create_socket'),
+
+    # give Mic Bowman's conf-monitor service read-only access to root fs
+    # and the ability to run df
+    ('idsl_monitor', 'mount_dir "root:/", pass, "ro"'),
+    ('idsl_monitor', 'unmount'),
+    ('idsl_monitor', 'exec "root", pass, "/bin/df", "-P", "/", "/vservers", none'),
+
+    # give Shark access to port 111 to run portmap
+    # and port 955 to run mount
+    ('nyu_shkr', 'bind_socket'),
+    ('nyu_shkr', 'mount_dir "nfs:**:**"'),
+    ('nyu_shkr', 'exec "root", pass, "/bin/umount", "-l", "/vservers/nyu_shkr/**", none'),
+
+    # give tsinghua_lgh access to restricted ports
+    ('tsinghua_lgh', 'bind_socket'),
+
+    # CoDeeN needs port 53 too
+    ('princeton_codeen', 'bind_socket sockname=53:*'),
+
+    # give ucin_load access to /var/log/wtmp
+    ('ucin_load', 'open_file file=/var/log/wtmp*'),
+
+    # give google_highground permission to bind port 81 (and raw sockets)
+    ('google_highground', 'bind_socket'),
+
+    # pl_conf needs access to port 814
+    ('pl_conf', 'bind_socket sockname=814:*'),
+    ('pl_conf', 'open file=/home/*/.ssh/authorized_keys'),
+
+    # give princeton_visp permission to read all packets sent through the
+    # tap0 device
+    ('princeton_visp', 'open file=/dev/net/tun, flags=rw'),
+
+    # The PLB group needs the BGP port
+    ('princeton_iias', 'bind_socket sockname=179:*'),
+    ('princeton_visp', 'bind_socket sockname=179:*'),
+    ('mit_rcp', 'bind_socket sockname=179:*'),
+    ('princeton_bgpmux', 'bind_socket sockname=179:*'),
+    ('princeton_bgpmux2', 'bind_socket sockname=179:*'),
+
+    # PL-VINI group
+    ('mit_rcp', 'exec "root", pass, "/usr/bin/chrt"'),
+    ('princeton_iias', 'exec "root", pass, "/usr/bin/chrt"'),
+
+    # Tycoon needs access to /etc/passwd to determine Slicename->XID mappings
+    ('hplabs_tycoon_aucd', 'open_file file=/etc/passwd'),
+]
+
+for slice, op in proper_ops:
+    try:
+        AddSliceAttribute(slice, 'proper_op', op)
+    except Exception, err:
+        print "Warning: %s:" % slice, err
+
+initscripts = dict([(initscript['initscript_id'], initscript) for initscript in [{'initscript_id': 8, 'script': 'IyEgL2Jpbi9zaA0KDQojIDxQcm9ncmFtIE5hbWU+DQojICAgIGJpbmRzY3JpcHQNCiMNCiMgPEF1dGhvcj4NCiMgICAgSmVmZnJ5IEpvaG5zdG9uIGFuZCBKZXJlbXkgUGxpY2h0YQ0KIw0KIyA8UHVycG9zZT4NCiMgICAgRG93bmxvYWRzIGFuZCBpbnN0YWxscyBzdG9yayBvbiBhIG5vZGUuDQoNCiMgc2F2ZSBvcmlnaW5hbCBQV0QNCk9MRFBXRD0kUFdEDQoNCiMgZXJyb3IgcmVwb3J0aW5nIGZ1bmN0aW9uDQplcnJvcigpDQp7DQogICBlY2hvDQogICBlY2hvICJQbGVhc2UgRS1tYWlsIHN0b3JrLXN1cHBvcnRAY3MuYXJpem9uYS5lZHUgaWYgeW91IGJlbGlldmUgeW91IGhhdmUiIA0KICAgZWNobyAicmVjZWl2ZWQgdGhpcyBtZXNzYWdlIGluIGVycm9yLiINCg0KICAgIyBnZXQgcmlkIG9mIENFUlQgZmlsZQ0KICAgaWYgWyAtZiAkQ0VSVCBdDQogICB0aGVuDQogICAgICBybSAtZiAkQ0VSVCA+IC9kZXYvbnVsbA0KICAgZmkNCg0KICAgIyByZXN0b3JlIG9yaWdpbmFsIFBXRA0KICAgY2QgJE9MRFBXRA0KICAgZXhpdCAxDQp9DQoNCkNFUlQ9YHB3ZGAvdGVtcGNydGZpbGUNCg0KI2Z1bmN0aW9ucw0KDQojIyMNCiMjIyBjcmVhdGVDZXJ0aWZpY2F0ZSgpDQojIyMgICAgcHJpbnRzIG91dCB0aGUgZXF1aWZheCBjZXJ0aWZpY2F0ZSB0byB1c2UgYW5kIHN0b3Jlcw0KIyMjICAgIHRoZSBmaWxlIG5hbWUgaW4gJENFUlQNCiMjIw0KZnVuY3Rpb24gY3JlYXRlQ2VydGlmaWNhdGUoKXsNCmNhdCA+ICRDRVJUIDw8RVFVSUZBWA0KLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlDa0RDQ0FmbWdBd0lCQWdJQkFUQU5CZ2txaGtpRzl3MEJBUVFGQURCYU1Rc3dDUVlEVlFRR0V3SlYNClV6RWNNQm9HQTFVRUNoTVRSWEYxYVdaaGVDQlRaV04xY21VZ1NXNWpMakV0TUNzR0ExVUVBeE1rUlhGMQ0KYVdaaGVDQlRaV04xY21VZ1IyeHZZbUZzSUdWQ2RYTnBibVZ6Y3lCRFFTMHhNQjRYRFRrNU1EWXlNVEEwDQpNREF3TUZvWERUSXdNRFl5TVRBME1EQXdNRm93V2pFTE1Ba0dBMVVFQmhNQ1ZWTXhIREFhQmdOVkJBb1QNCkUwVnhkV2xtWVhnZ1UyVmpkWEpsSUVsdVl5NHhMVEFyQmdOVkJBTVRKRVZ4ZFdsbVlYZ2dVMlZqZFhKbA0KSUVkc2IySmhiQ0JsUW5WemFXNWxjM01nUTBFdE1UQ0JuekFOQmdrcWhraUc5dzBCQVFFRkFBT0JqUUF3DQpnWWtDZ1lFQXV1Y1hrQUpsc1RSVlBFbkNVZFhmcDlFM2o5SG5nWE5CVW1DYm5hRVhKbml0eDdIb0pwUXkNCnRkNHpqVG92Mi9LYWVscHptS05jNmZ1S2N4dGM1OE8vZ0d6TnFmVFdLOEQzK1ptcVk2S3hSd0lQMU9SUg0KT2hJOGJJcGFWSVJ3MjhIRmtNOXlSY3VvV2NETk01MC9vNWJyaFRNaEhENGVQbUJ1ZHB4bmhjWEl3MkVDDQpBd0VBQWFObU1HUXdFUVlKWUlaSUFZYjRRZ0VCQkFRREFnQUhNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHcNCkh3WURWUjBqQkJnd0ZvQVV2cWlnZEhKUWEwUzN5U1BZKzZqL3MxZHJhR3d3SFFZRFZSME9CQllFRkw2bw0Kb0hSeVVHdEV0OGtqMlB1by83TlhhMmhzTUEwR0NTcUdTSWIzRFFFQkJBVUFBNEdCQUREaUFWR3F4K3BmDQoycm5RWlE4dzFqN2FEUlJKYnBHVEp4UXg3OFQzTFVYNDdNZS9va0VOSTdTUytSa0FaNzBCcjgzZ2NmeGENCnoyVEU0SmFZMEtOQTRnR0s3eWNIOFdVQmlrUXRCbVYxVXNDR0VDQWhYMnhyRDJ5dUNSeXY4cUlZTk1SMQ0KcEhNYzhZM2M3NjM1czNhMGtyL2NsUkFldnN2SU8xcUVZQmxXbEtsVg0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLSANCkVRVUlGQVgNCn0NCg0KIyMjDQojIyMgb3ZlcldyaXRlQ29uZigpDQojIyMJb3ZlcndyaXRlIHRoZSBkZWZhdWx0IHN0b3JrLmNvbmYgZmlsZQ0KIyMjICAgICB0aGF0IHdhcyBpbnN0YWxsZWQgYnkgdGhlIHJwbSBwYWNrYWdlLg0KIyMjICAgICB0aGlzIGlzIGEgdGVtcG9yYXJ5IGhhY2sgYmVjYXVzZSBJIG5lZWQNCiMjIyAgICAgdG8gY2hhbmdlIHRoZSBuZXN0cG9ydCBhbmQgSSBkb250IGtub3cNCiMjIyAgICAgZW5vdWdoIHRvIHJlcGFja2FnZSB0aGUgcnBtIHdpdGggdGhlDQojIyMgICAgIGNvcnJlY3Qgc2V0dGluZ3MNCmZ1bmN0aW9uIG92ZXJXcml0ZUNvbmYoKXsNCmNhdCA+IC91c3IvbG9jYWwvc3RvcmsvZXRjL3N0b3JrLmNvbmYgPDxFTkRPRkZJTEUNCnBhY21hbj0vdXNyL2xvY2FsL3N0b3JrL2Jpbi9wYWNtYW4NCmR0ZC1wYWNrYWdlcz0vdXNyL2xvY2FsL3N0b3JrL2Jpbi9wYWNrYWdlcy5kdGQNCmR0ZC1ncm91cHM9L3Vzci9sb2NhbC9zdG9yay9iaW4vZ3JvdXBzLmR0ZA0Kc3RvcmtuZXN0dXBkYXRlbGlzdGVuZXJwb3J0PTY0OQ0KDQojYml0dG9ycmVudHRyYWNrZXJob3N0PXF1YWRydXMuY3MuYXJpem9uYS5lZHUNCmJpdHRvcnJlbnR0cmFja2VyaG9zdD1ucjA2LmNzLmFyaXpvbmEuZWR1DQoNCmJpdHRvcnJlbnR0cmFja2VycG9ydD02ODgwDQpiaXR0b3JyZW50dXBsb2FkcmF0ZT0wDQpiaXR0b3JyZW50c2VlZGxvb2t1cHRpbWVvdXQ9MzANCg0KI3BhY2thZ2VyZXBvc2l0b3J5ID0gcXVhZHJ1cy5jcy5hcml6b25hLmVkdS9QbGFuZXRMYWIvVjN8ZGlzdCwgc3RhYmxlDQpwYWNrYWdlcmVwb3NpdG9yeSA9IG5yMDYuY3MuYXJpem9uYS5lZHUvUGxhbmV0TGFiL1YzfGRpc3QsIHN0YWJsZQ0KI3BhY2thZ2VpbmZvcmVwb3NpdG9yeSA9IHF1YWRydXMuY3MuYXJpem9uYS5lZHUvUGxhbmV0TGFiL1YzL3N0b3JrLmluZm8NCnBhY2thZ2VpbmZvcmVwb3NpdG9yeSA9IG5yMDYuY3MuYXJpem9uYS5lZHUvUGxhbmV0TGFiL1YzL3N0b3JrLmluZm8NCg0KdXNlcm5hbWUgPSBQbGFuZXRMYWINCnB1YmxpY2tleWZpbGUgPSAvdXNyL2xvY2FsL3N0b3JrL3Zhci9rZXlzL1BsYW5ldExhYi5wdWJsaWNrZXkNCnBhY2thZ2VtYW5hZ2VycyA9IG5lc3RycG0sIHJwbSwgdGFyZ3oNCnRyYW5zZmVybWV0aG9kPSBuZXN0LGJpdHRvcnJlbnQsY29ibGl0eixjb3JhbCxodHRwLGZ0cA0KbmVzdHBvcnQ9NjAwMA0KdGFycGFja2luZm9wYXRoPS91c3IvbG9jYWwvc3RvcmsvdmFyL3RhcmluZm8NCkVORE9GRklMRQ0KfSANCg0KDQojIyMNCiMjIyBkb3dubG9hZE5SMDYoKQ0KIyMjICAgIGRvd25sb2FkIGEgZmlsZSBmcm9tIG5yMDYgdXNpbmcgY3VybA0KIyMjDQojIyMgYXJnczogDQojIyMgICAgICAgLSB0aGUgcGF0aCBvZiB0aGUgZmlsZSB5b3Ugd2lzaCB0byBkb3dubG9hZA0KIyMjICAgICAgICAgcmVsYXRpdmUgZnJvbSBodHRwczovL25yMDYuY3MuYXJpem9uYS5lZHUNCiMjIyAgICAgICAtIHRoZSBmaWxlIHRvIHNhdmUgaXQgdG8NCiMjIyAgICAgICAtIHJldHVybmVkIHZhbHVlIGFzIHNwZWNpZmllZCBpbiB2ZXJpZnlEb3dubG9hZA0KZnVuY3Rpb24gZG93bmxvYWROUjA2KCl7DQogICAgY3VybCAtLWNhY2VydCAkQ0VSVCBodHRwczovL25yMDYuY3MuYXJpem9uYS5lZHUvJDEgLW8gJDIgMj4vZGV2L251bGwNCiAgICB2ZXJpZnlEb3dubG9hZCAkMiAkMw0KfQ0KDQojIyMNCiMjIyB2ZXJpZnlEb3dubG9hZCgpDQojIyMgICAgIHZlcmlmeSB0aGF0IGEgZmlsZSB0aGF0IHdhcyBqdXN0IGRvd25sb2FkIHdpdGggZG93bmxvYWROUjA2DQojIyMgICAgIHdhcyBkb3dubG9hZCBjb3JyZWN0bHkuIFNpbmNlIHdlIGFyZSBnZXR0aW5nIHN0dWZmIGZyb20gYQ0KIyMjICAgICBodHRwIHNlcnZlciB3ZSBhcmUgYXNzdW1pbmcgdGhhdCBpZiB3ZSBnZXQgYSA0MDQgcmVzcG9uc2UNCiMjIyAgICAgdGhhdCB0aGUgcGFnZSB3ZSB3YW50IGRvZXMgbm90IGV4aXN0LiBBbHNvLCBpZiB0aGUgb3V0cHV0IGZpbGUNCiMjIyAgICAgZG9lcyBub3QgZXhpc3QgdGhhdCBtZWFucyB0aGF0IG9ubHkgaGVhZGVycyB3ZXJlIHJldHVybmVkDQojIyMgICAgIHdpdGhvdXQgYW55IGNvbnRlbnQuIHRoaXMgdG9vIGlzIGEgaW52YWxpZCBmaWxlIGRvd25sb2FkDQojIyMNCiMjIyBhcmdzOg0KIyMjICAgICAgIC0gdGhlIGZpbGUgdG8gdmVyaWZ5DQojIyMgICAgICAgLSByZXR1cm4gdmFyaWFibGUsIHdpbGwgaGF2ZSAxIGlmIGZhaWwgMCBpZiBnb29kDQojIyMNCmZ1bmN0aW9uIHZlcmlmeURvd25sb2FkKCl7DQogICAgZXZhbCAiJDI9MCINCiAgICBpZiBbICEgLWYgJDEgXTsNCiAgICB0aGVuDQogICAgICAgIGV2YWwgIiQyPTEiDQogICAgZWxpZiBncmVwICc0MDQgTm90IEZvdW5kJyAkMSA+IC9kZXYvbnVsbA0KICAgIHRoZW4NCglybSAtZiAkMQ0KICAgICAgICBldmFsICIkMj0xIg0KICAgIGVsc2UNCiAgICAgICAgZXZhbCAiJDI9MCINCiAgICBmaQ0KfQ0KDQoNCiMgY2hlY2sgZm9yIHJvb3QgdXNlcg0KaWYgWyAkVUlEIC1uZSAiMCIgXQ0KdGhlbg0KICAgZWNobyAiWW91IG11c3QgcnVuIHRoaXMgcHJvZ3JhbSB3aXRoIHJvb3QgcGVybWlzc2lvbnMuLi4iDQogICBlcnJvcg0KZmkgICANCiANCiMgY2xlYW4gdXAgaW4gY2FzZSB0aGlzIHNjcmlwdCB3YXMgcnVuIGJlZm9yZSBhbmQgZmFpbGVkDQpybSAtcmYgL3RtcC9zdG9yayAmPiAvZGV2L251bGwNCg0KIyBjcmVhdGUgL3RtcC9zdG9yayBkaXJlY3RvcnkNCm1rZGlyIC90bXAvc3RvcmsgDQppZiBbICQ/IC1uZSAiMCIgXQ0KdGhlbg0KICAgZWNobw0KICAgZWNobyAiQ291bGQgbm90IGNyZWF0ZSB0aGUgL3RtcC9zdG9yayBkaXJlY3RvcnkuLi4iDQogICBlcnJvcg0KZmkNCg0KIyBleHBvcnQgb3VyIHJvb3QgZGlyZWN0b3J5IHRvIFN0b3JrDQplY2hvICJhcml6b25hX3N0b3JrMiIgPiAvLmV4cG9ydGRpcg0KaWYgWyAkPyAtbmUgIjAiIF0NCnRoZW4NCiAgIGVjaG8NCiAgIGVjaG8gIkNvdWxkIG5vdCBjcmVhdGUgdGhlIC8uZXhwb3J0ZGlyIGZpbGUuLi4iDQogICBlcnJvcg0KZmkNCiANCiMgdGVsbCBzdG9yayB0aGF0IHdlIHdhbnQgdG8gYmUgc2VydmVkDQppZiBbIC1mIC9ldGMvc2xpY2VuYW1lIF0NCnRoZW4NCiAgIFNMSUNFTkFNRT1gY2F0IC9ldGMvc2xpY2VuYW1lYA0KZWxzZSANCiAgIFNMSUNFTkFNRT0kVVNFUg0KZmkNCndnZXQgLU8gL3RtcC9zdG9yay8kU0xJQ0VOQU1FICJodHRwOi8vbG9jYWxob3N0OjY0OC8kU0xJQ0VOQU1FXCRiaW5kc2NyaXB0Ig0KDQojIHZlcmlmeSB0aGF0IHRoZSBkb3dubG9hZCB3YXMgc3VjY2Vzc2Z1bA0KaWYgWyAhIC1mIC90bXAvc3RvcmsvJFNMSUNFTkFNRSAtbyAkPyAtbmUgMCBdDQp0aGVuDQogICBlY2hvDQogICBlY2hvICJTdG9yayBkb2Vzbid0IHNlZW0gdG8gYmUgcnVubmluZyBvbiB0aGlzIG5vZGUuLi4iDQogICBlcnJvcg0KZmkNCg0KIyB3YWl0IGZvciBzdG9yayBzbGljZSANCmVjaG8gIldhaXRpbmcgZm9yIFN0b3JrIHRvIGFjY2VwdCBvdXIgYmluZGluZy4uLiINCndoaWxlIFsgISAtZiAvdG1wL3N0b3JrL3N0b3JrX3NheXNfZ28gXQ0KZG8NCiAgIHNsZWVwIDENCmRvbmUNCg0KIyBjaGFuZ2UgUFdEIHRvIHRoZSAvdG1wL3N0b3JrIGRpcmVjdG9yeSANCmNkIC90bXAvc3RvcmsNCmlmIFsgJD8gLW5lICIwIiBdDQp0aGVuDQogICBlY2hvDQogICBlY2hvICJDb3VsZCBub3QgYWNjZXNzIHRoZSAvdG1wL3N0b3JrIGRpcmVjdG9yeS4uLiINCiAgIGVycm9yDQpmaQ0KDQojIGNvbmZpcm0gdGhhdCBwYWNrYWdlcyB0byBiZSBpbnN0YWxsZWQgYWN0dWFsbHkgZXhpc3QNCmlmIGVjaG8gKi5ycG0gfCBncmVwICcqJyA+IC9kZXYvbnVsbA0KdGhlbg0KICAgZWNobw0KICAgZWNobyAiRXJyb3I6IFN0b3JrIHBhY2thZ2UgZG93bmxvYWQgZmFpbGVkLi4uIg0KICAgZXJyb3INCmZpDQoNCiMgcmVtb3ZlIFN0b3JrIHBhY2thZ2VzIGFuZCBmaWxlcw0KZWNobw0KZWNobyAiUmVtb3ZpbmcgU3RvcmsgZmlsZXMuLi4iDQoNCiMgYnVpbGQgYSBsaXN0IG9mIHBhY2thZ2VzIHRvIHJlbW92ZQ0KcGFja2FnZXM9IiINCmZvciBmaWxlbmFtZSBpbiAqLnJwbQ0KZG8NCiAgIyBjb252ZXJ0IGZpbGVuYW1lIHRvIGEgcGFja2FnZSBuYW1lDQogIHBhY2s9YHJwbSAtcXAgLS1xZiAiJXtOQU1FfVxuIiAkZmlsZW5hbWVgDQogIGlmIFsgJD8gLWVxICIwIiBdDQogIHRoZW4NCiAgICBwYWNrYWdlcz0iJHBhY2thZ2VzICRwYWNrIg0KICBmaQ0KZG9uZSAgIA0KDQojIHJlbW92ZSBvbGQgU3RvcmsgcGFja2FnZXMNCnJwbSAtZSAkcGFja2FnZXMgJj4gL2Rldi9udWxsDQoNCiMgcmVtb3ZlIGFueXRoaW5nIGxlZnQgaW4gL3Vzci9sb2NhbC9zdG9yay9iaW4NCnJtIC1yZiAvdXNyL2xvY2FsL3N0b3JrL2Jpbi8qICY+IC9kZXYvbnVsbCANCg0KIyBpbnN0YWxsIFN0b3JrIHBhY2thZ2VzDQplY2hvDQplY2hvICJJbnN0YWxsaW5nIHBhY2thZ2VzLi4uIiANCg0KIyBidWlsZCBhIGxpc3Qgb2YgcGFja2FnZXMgdG8gaW5zdGFsbA0KcGFja2FnZXM9IiINCmZvciBmaWxlbmFtZSBpbiAqLnJwbQ0KZG8NCiAgcGFja2FnZXM9IiRwYWNrYWdlcyAkZmlsZW5hbWUiDQpkb25lICAgDQoNCiMgaW5zdGFsbCB0aGUgbmV3IHN0b3JrIHBhY2thZ2VzDQpycG0gLWkgJHBhY2thZ2VzDQoNCiMgcmVwb3J0IHBhY2thZ2UgaW5zdGFsbGF0aW9uIGVycm9ycw0KaWYgWyAkPyAtbmUgIjAiIF0NCnRoZW4NCiAgZWNobyAiV2FybmluZzogUG9zc2libGUgZXJyb3IgaW5zdGFsbGluZyBTdG9yayBwYWNrYWdlcy4uLiINCmZpDQoNCiMgcmVzdG9yZSBvcmlnaW5hbCBQV0QNCmNkICRPTERQV0QNCg0KIyBjbGVhbiB1cCB0ZW1wb3JhcnkgZmlsZXMNCnJtIC1yZiAvdG1wL3N0b3JrICY+IC9kZXYvbnVsbA0KDQojIFNFRSBUTy1ETyAxDQojY3JlYXRlIHRoZSBlcXVpZmF4IGNlcnRpZmljYXRlIHRvIHVzZSBmb3IgY3VybA0KI2NyZWF0ZUNlcnRpZmljYXRlDQoNCiMgVE8tRE8gMQ0KIyBpbXBsZW1lbnQgdGhlIGJlbG93IGluIHRoZSBiZWdnaW5pbmcgb2Ygc3RvcmsucHkNCiNhdHRlbXB0IHRvIGRvd25sb2FkIHRoZSB1c2VycyBwdWJsaWMga2V5IGZyb20gdGhlIHJlcG9zaXRvcnkNCiNkb3dubG9hZE5SMDYgInVzZXItdXBsb2FkL3B1YmtleXMvJFNMSUNFTkFNRS5wdWJsaWNrZXkiICIvdXNyL2xvY2FsL3N0b3JrL3Zhci8kU0xJQ0VOQU1FLnB1YmxpY2tleSIgUkVUDQoNCiNpZiBbICRSRVQgLW5lIDAgXTsNCiN0aGVuDQojICAgZWNobw0KIyAgIGVjaG8gIkNvdWxkIG5vdCBmZXRjaCB5b3VyIHB1YmxpYyBrZXkgZnJvbSB0aGUgcmVwb3NpdG9yeS4iDQojICAgZWNobyAiSWYgeW91IHdhbnQgdG8gdXBsb2FkIG9uZSBmb3IgdGhlIG5leHQgdGltZSB5b3UgcnVuIg0KIyAgIGVjaG8gInRoZSBpbml0c2NyaXB0IHBsZWFzZSB2aXNpdCINCiMgICBlY2hvICJodHRwOi8vbnIwNi5jcy5hcml6b25hLmVkdS90ZXN0cGhwL3VwbG9hZC5waHAiDQojICAgZWNobw0KI2ZpDQoNCiNhdHRlbXB0IHRvIGRvd25sb2FkIHRoZSB1c2VycyBzdG9yay5jb25mIGZpbGUgZnJvbSB0aGUgcmVwb3NpdG9yeQ0KI2Rvd25sb2FkTlIwNiAidXNlci11cGxvYWQvY29uZi8kU0xJQ0VOQU1FLnN0b3JrLmNvbmYiICIvdXNyL2xvY2FsL3N0b3JrL2V0Yy9zdG9yay5jb25mLnVzZXJzIiBSRVQNCg0KI2lmIFsgJFJFVCAtbmUgMCBdOw0KI3RoZW4NCiMgICBlY2hvDQojICAgZWNobyAiQ291bGQgbm90IGZldGNoIHlvdXIgc3RvcmsuY29uZiBmaWxlIGZyb20gdGhlIHJlcG9zaXRvcnkuIg0KIyAgIGVjaG8gIklmIHlvdSB3YW50IHRvIHVwbG9hZCBvbmUgZm9yIHRoZSBuZXh0IHRpbWUgeW91IHJ1biINCiMgICBlY2hvICJ0aGUgaW5pdHNjcmlwdCBwbGVhc2UgdmlzaXQiDQojICAgZWNobyAiaHR0cDovL25yMDYuY3MuYXJpem9uYS5lZHUvdGVzdHBocC91cGxvYWQucGhwIg0KIyAgIGVjaG8gIlN0b3JrIHdpbGwgd29yayB3aXRob3V0IGEgY29uZmlndXJhdGlvbiBmaWxlIGJ1dCB0byBtYWtlIG9uZSINCiMgICBlY2hvICJwbGVhc2UgcGxhY2UgYSBmaWxlIG5hbWVkIHN0b3JrLmNvbmYgaW4gL3Vzci9sb2NhbC9zdG9yay9ldGMiDQojICAgZWNobyAicmVmZXIgdG8gdGhlIG1hbnVhbCBmb3IgbW9yZSBkaXJlY3Rpb25zIG9yIGVtYWlsOiINCiMgICBlY2hvICJzdG9yay1zdXBwb3J0QGNzLmFyaXpvbmEuZWR1IGZvciBhZGRpdGlvbmFsIGFzc2lzdGFuY2UuIg0KIyAgIGVjaG8NCiNmaQ0KDQojZG9udCBuZWVkIHRvIG92ZXJ3cml0ZSB0aGUgZGVmYXVsdCBjb25mIGZpbGUNCiNiZWNhdXNlIGl0IHNob3VsZCBiZSBmaXhlZCBpbiB0aGUgbmV3IHJwbXMNCiNvdmVyV3JpdGVDb25mDQoNCiMgcnVuIHN0b3JrIHRvIHVwZGF0ZSBrZXlmaWxlcyBhbmQgZG93bmxvYWQgcGFja2FnZSBsaXN0cw0KZWNobw0KZWNobyAiQXR0ZW1wdGluZyB0byBjb21tdW5pY2F0ZSB3aXRoIHN0b3JrLi4uIg0KaWYgc3RvcmsgDQp0aGVuDQogICBlY2hvDQogICBlY2hvICJDb25ncmF0dWxhdGlvbnMsIHlvdSBoYXZlIHN1Y2Nlc3NmdWxseSBib3VuZCB0byBzdG9yayEiDQogICBlY2hvDQogICBlY2hvICJGb3IgaGVscCwgeW91IG1heSB0eXBlIHN0b3JrIC0taGVscCINCiAgIGVjaG8NCiAgICNlY2hvICJUaGVyZSBpcyBhbHNvIGEgc3RvcmtxdWVyeSBjb21tYW5kIHRoYXQgd2lsbCBwcm92aWRlIGluZm9ybWF0aW9uIg0KICAgI2VjaG8gImFib3V0IHBhY2thZ2VzIGluIHRoZSByZXBvc2l0b3J5LiINCiAgIGVjaG8NCiAgIGVjaG8gIkZvciBtb3JlIGhlbHAsIHZpc2l0IHRoZSBzdG9yayBwcm9qZWN0IG9ubGluZSBhdCINCiAgIGVjaG8gImh0dHA6Ly93d3cuY3MuYXJpem9uYS5lZHUvc3RvcmsvLiAgUGxlYXNlIGNvbnRhY3QiDQogICBlY2hvICJzdG9yay1zdXBwb3J0QGNzLmFyaXpvbmEuZWR1IGZvciBhZGRpdGlvbmFsIGFzc2lzdGFuY2UuIiANCiAgICNybSAtZiAkQ0VSVCA+IC9kZXYvbnVsbA0KZWxzZQ0KICAgZWNobw0KICAgZWNobyAiQW4gZXJyb3Igb2NjdXJyZWQgZHVyaW5nIGluc3RhbGwgZmluYWxpemF0aW9uLi4uICBQbGVhc2UgY29udGFjdCINCiAgIGVjaG8gInN0b3JrLXN1cHBvcnRAY3MuYXJpem9uYS5lZHUgZm9yIGFzc2lzdGFuY2UuIg0KICAgI3JtIC1mICRDRVJUID4gL2Rldi9udWxsDQogICBleGl0IDENCmZpDQoNCiMgZG9uZQ0KZXhpdCAwDQo=', 'name': 'arizona_stork_2', 'encoding': 'base64'}, {'initscript_id': 9, 'script': 'IyEvYmluL2Jhc2gNCmNkIC8NCnJtIC1mIHN0YXJ0X3B1cnBsZQ0Kd2dldCBodHRwOi8vd3d3LmNzLnByaW5jZXRvbi5lZHUvfmRlaXNlbnN0L3B1cnBsZS9zdGFydF9wdXJwbGUNCmNobW9kIDc1NSBzdGFydF9wdXJwbGUNCnN1IHByaW5jZXRvbl9wdXJwbGUgLWMgJy4vc3RhcnRfcHVycGxlJw0K', 'name': 'princeton_purple', 'encoding': 'base64'}, {'initscript_id': 6, 'script': 'IyEgL2Jpbi9zaA0KDQojIHNhdmUgb3JpZ2luYWwgUFdEDQpPTERQV0Q9JFBXRA0KDQojIGVycm9yIHJlcG9ydGluZyBmdW5jdGlvbg0KZXJyb3IoKQ0Kew0KICAgZWNobw0KICAgZWNobyAiUGxlYXNlIEUtbWFpbCBzdG9yay1zdXBwb3J0QGNzLmFyaXpvbmEuZWR1IGlmIHlvdSBiZWxpZXZlIHlvdSBoYXZlIiANCiAgIGVjaG8gInJlY2VpdmVkIHRoaXMgbWVzc2FnZSBpbiBlcnJvci4iDQoNCiAgICMgcmVzdG9yZSBvcmlnaW5hbCBQV0QNCiAgIGNkICRPTERQV0QNCiAgIGV4aXQgMQ0KfQ0KDQojIGNoZWNrIGZvciByb290IHVzZXINCmlmIFsgJFVJRCAtbmUgIjAiIF0NCnRoZW4NCiAgIGVjaG8gJ1lvdSBtdXN0IGJlIHJvb3QgdG8gcnVuIHRoaXMgcHJvZ3JhbS4uLicNCiAgIGVycm9yDQpmaSAgIA0KIA0KIyBDbGVhbiB1cCBpbiBjYXNlIEkgcmFuIHRoaXMgYmVmb3JlDQpybSAtZiAvdG1wL3N0b3JrKiA+IC9kZXYvbnVsbCAyPiYxDQoNCiMgRmlyc3Qgb2YgYWxsIGV4cG9ydCBvdXIgcm9vdCBkaXJlY3RvcnkgdG8gU3RvcmsNCmVjaG8gImFyaXpvbmFfc3RvcmsiID4gLy5leHBvcnRkaXINCiANCiMgTm93IHRlbGwgc3RvcmsgdGhhdCB3ZSB3YW50IHRvIGJlIHNlcnZlZA0KaWYgWyAtZiAvZXRjL3NsaWNlbmFtZSBdDQp0aGVuDQogICBTTElDRU5BTUU9YGNhdCAvZXRjL3NsaWNlbmFtZWANCmVsc2UgDQogICBTTElDRU5BTUU9JFVTRVINCmZpDQoNCndnZXQgaHR0cDovL2xvY2FsaG9zdDo2NDAvJFNMSUNFTkFNRQ0KDQojIGNoZWNrIHRvIG1ha2Ugc3VyZSB0aGUgZG93bmxvYWQgd2FzIHN1Y2Nlc3NmdWwNCmlmIFsgISAtZiAkU0xJQ0VOQU1FIC1vICQ/IC1uZSAwIF0NCnRoZW4NCiAgIGVjaG8NCiAgIGVjaG8gIlN0b3JrIGRvZXNuJ3Qgc2VlbSB0byBiZSBydW5uaW5nIG9uIHRoaXMgbm9kZS4uLiINCiAgIGVycm9yDQpmaQ0KDQojIHdhaXQgZm9yIHN0b3JrIHNsaWNlIA0KZWNobyAiV2FpdGluZyBmb3IgU3RvcmsgdG8gYWNjZXB0IG91ciBiaW5kaW5nLi4uIg0Kd2hpbGUgWyAhIC1mIC90bXAvc3Rvcmtfc2F5c19nbyBdDQpkbw0KICAgc2xlZXAgMQ0KZG9uZQ0KDQojIGNoYW5nZSBQV0QgdG8gdGhlIC90bXAgZGlyZWN0b3J5IA0KY2QgL3RtcA0KaWYgWyAkPyAtbmUgIjAiIF0NCnRoZW4NCiAgIGVjaG8NCiAgIGVjaG8gIkNvdWxkIG5vdCBhY2Nlc3MgdGhlIC90bXAgZGlyZWN0b3J5Li4uIg0KICAgZXJyb3INCmZpDQoNCiMgY29uZmlybSB0aGF0IHBhY2thZ2VzIHRvIGJlIGluc3RhbGxlZCBhY3R1YWxseSBleGlzdA0KaWYgZWNobyAqLnJwbSB8IGdyZXAgJyonID4gL2Rldi9udWxsDQp0aGVuDQogICBlY2hvDQogICBlY2hvICJFcnJvcjogU3RvcmsgcGFja2FnZSBkb3dubG9hZCBmYWlsZWQuLi4iDQogICBlcnJvcg0KZmkNCg0KIyBpbnN0YWxsIFN0b3JrIHBhY2thZ2VzDQplY2hvICJJbnN0YWxsaW5nIHBhY2thZ2VzLi4uIiANCmZvciBwYWNrIGluICoucnBtDQpkbw0KICAgIyByZW1vdmUgdGhlIG9sZCBzdG9yayBwYWNrYWdlLCBpZiBhbnkNCiAgIHJwbSAtZSBgcnBtIC1xcCAtLXFmICIle05BTUV9XG4iICRwYWNrYCA+IC9kZXYvbnVsbCAyPiYxDQoNCiAgICMgcmVtb3ZlIGFueXRoaW5nIGxlZnQgaW4gL3Vzci9sb2NhbC9zdG9yay9iaW4NCiAgIHJtIC1yZiAvdXNyL2xvY2FsL3N0b3JrL2Jpbi8qID4gL2Rldi9udWxsIDI+JjENCg0KICAgIyBpbnN0YWxsIHRoZSBuZXcgc3RvcmsgcGFja2FnZQ0KICAgcnBtIC1pICRwYWNrDQoNCiAgICMgcmVwb3J0IHBhY2thZ2UgaW5zdGFsbGF0aW9uIGVycm9ycw0KICAgaWYgWyAkPyAtbmUgIjAiIF0NCiAgIHRoZW4NCiAgICAgZWNobyAiV2FybmluZzogUG9zc2libGUgZXJyb3IgaW5zdGFsbGluZyBTdG9yayBwYWNrYWdlOiAkcGFjay4uLiINCiAgIGZpDQpkb25lDQoNCiMgcmVzdG9yZSBvcmlnaW5hbCBQV0QNCmNkICRPTERQV0QNCg0KIyBjbGVhbiB1cCB0ZW1wb3JhcnkgZmlsZXMNCnJtIC1mIC90bXAvc3RvcmsqID4gL2Rldi9udWxsIDI+JjENCnJtICRTTElDRU5BTUUqIA0KDQojIHJ1biBzdG9yayB0byB1cGRhdGUga2V5ZmlsZXMgYW5kIGRvd25sb2FkIHBhY2thZ2UgbGlzdHMNCmVjaG8gIkF0dGVtcHRpbmcgdG8gY29tbXVuaWNhdGUgd2l0aCBzdG9yay4uLiINCmlmIHN0b3JrIA0KdGhlbg0KICAgZWNobw0KICAgZWNobyAiQ29uZ3JhdHVsYXRpb25zLCB5b3UgaGF2ZSBzdWNjZXNzZnVsbHkgYm91bmQgdG8gc3RvcmshIg0KICAgZWNobw0KICAgZWNobyAiRm9yIGhlbHAsIHlvdSBtYXkgdHlwZSBzdG9yayAtLWhlbHAgIg0KICAgZWNobw0KICAgZWNobyAiVGhlcmUgaXMgYWxzbyBhIHN0b3JrcXVlcnkgY29tbWFuZCB0aGF0IHdpbGwgcHJvdmlkZSBpbmZvcm1hdGlvbiINCiAgIGVjaG8gImFib3V0IHBhY2thZ2VzIGluIHRoZSByZXBvc2l0b3J5LiINCiAgIGVjaG8NCiAgIGVjaG8gIkZvciBtb3JlIGhlbHAsIHZpc2l0IHRoZSBzdG9yayBwcm9qZWN0IG9ubGluZSBhdCINCiAgIGVjaG8gImh0dHA6Ly93d3cuY3MuYXJpem9uYS5lZHUvc3RvcmsvLiAgUGxlYXNlIGNvbnRhY3QiDQogICBlY2hvICJzdG9yay1zdXBwb3J0QGNzLmFyaXpvbmEuZWR1IGZvciBhZGRpdGlvbmFsIGFzc2lzdGFuY2UuIiANCmVsc2UNCiAgIGVjaG8NCiAgIGVjaG8gIkFuIGVycm9yIG9jY3VycmVkIGR1cmluZyBpbnN0YWxsIGZpbmFsaXphdGlvbi4uLiAgUGxlYXNlIGNvbnRhY3QiDQogICBlY2hvICJzdG9yay1zdXBwb3J0QGNzLmFyaXpvbmEuZWR1IGZvciBhc3Npc3RhbmNlLiINCiAgIGV4aXQgMQ0KZmkNCg0KIw0KIyBIZWxsbyBXb3JsZCBkZW1vIGNvZGUNCiMNCg0KIyBQdWJsaWMga2V5IGZvciB0aGlzIGRlbW8NCmNhdCA+L3Vzci9sb2NhbC9zdG9yay92YXIva2V5cy9oZWxsby5wdWJsaWNrZXkgPDwiRU9GIg0KLS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1Gd3dEUVlKS29aSWh2Y05BUUVCQlFBRFN3QXdTQUpCQU1XcVE3K2VxQVljNlRPSUJPbkJyRnZqYjlnRVViaWgNCkkxd0Nyeld4a09aa01BcXFmY1RuMW9tcCtLMGd0cUtBK3VaNEIzRGlQRXI0Q0V0Myt5MmJlMGtDQXdFQUFRPT0NCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQ0KRU9GDQpzZWQgLWkgLWUgJ3MvXnVzZXJuYW1lLiovdXNlcm5hbWUgPSBoZWxsby8nIC91c3IvbG9jYWwvc3RvcmsvZXRjL3N0b3JrLmNvbmYNCg0KIyBJbnN0YWxsIFJQTQ0Kc3RvcmsgdXBncmFkZSBoZWxsbw0KDQojIGVuZA0KZXhpdCAwDQo=', 'name': 'princeton_hello_stork', 'encoding': 'base64'}, {'initscript_id': 10, 'script': 'IyEvYmluL2Jhc2gNCg0KIyBJbml0IHNjcmlwdCBmb3IgdGhlIFBsYW5ldExhYiAiSGVsbG8gV29ybGQiIGRlbW8gdXNpbmcgR29vZ2xlIEVhcnRoLg0KIyBJbnN0YWxscyBhIGNyb250YWIgZW50cnkgb24gdGhlIG5vZGUgdGhhdCBwaG9uZXMgaG9tZSB0byB0aGUgc2VydmVyDQojIGV2ZXJ5IHRocmVlIG1pbnV0ZXMuDQoNClNFUlZFUj0xMjguMTEyLjEzOS43Mzo4MDQyCQkjIHBsYW5ldGxhYi0zLmNzLnByaW5jZXRvbi5lZHUNCg0KL3Vzci9iaW4vY3VybCAtcyBodHRwOi8vJFNFUlZFUi8NCmVjaG8gIiovNSAqICogKiAqIC91c3IvYmluL2N1cmwgLXMgaHR0cDovLyRTRVJWRVIvIiB8IGNyb250YWIgLQ0KL3NiaW4vY2hrY29uZmlnIGNyb25kIG9uDQo=', 'name': 'princeton_hello', 'encoding': 'base64'}]])
+
+# Convert plc_initscript.initscript_id to raw initscript attribute
+for slice_attribute in GetSliceAttributes({'name': 'plc_initscript'}):
+    id = slice_attribute['slice_attribute_id']
+    slice_id = slice_attribute['slice_id']
+    initscript_id = int(slice_attribute['value'])
+
+    # Delete old attribute
+    DeleteSliceAttribute(id)
+
+    if initscript_id not in initscripts:
+        print "Warning: Missing initscript %d" % initscript_id
+        continue
+
+    initscript = base64.b64decode(initscripts[initscript_id]['script'])
+
+    # Add as initscript attribute
+    AddSliceAttribute(slice_id, 'initscript', initscript)
+
+# Add our custom yum.conf entries
+conf_file_id = AddConfFile({
+    'enabled': True,
+    'source': 'PlanetLabConf/yum.conf.php?gpgcheck=1&alpha',
+    'dest': '/etc/yum.conf',
+    'file_permissions': '644',
+    'file_owner': 'root',
+    'file_group': 'root',
+    'preinstall_cmd': '',
+    'postinstall_cmd': '',
+    'error_cmd': '',
+    'ignore_cmd_errors': False,
+    'always_update': False})
+AddConfFileToNodeGroup(conf_file_id, 'Alpha')
+
+conf_file_id = AddConfFile({
+    'enabled': True,
+    'source': 'PlanetLabConf/yum.conf.php?gpgcheck=1&beta',
+    'dest': '/etc/yum.conf',
+    'file_permissions': '644',
+    'file_owner': 'root',
+    'file_group': 'root',
+    'preinstall_cmd': '',
+    'postinstall_cmd': '',
+    'error_cmd': '',
+    'ignore_cmd_errors': False,
+    'always_update': False})
+AddConfFileToNodeGroup(conf_file_id, 'Beta')
+
+conf_file_id = AddConfFile({
+    'enabled': True,
+    'source': 'PlanetLabConf/yum.conf.php?gpgcheck=1&rollout',
+    'dest': '/etc/yum.conf',
+    'file_permissions': '644',
+    'file_owner': 'root',
+    'file_group': 'root',
+    'preinstall_cmd': '',
+    'postinstall_cmd': '',
+    'error_cmd': '',
+    'ignore_cmd_errors': False,
+    'always_update': False})
+AddConfFileToNodeGroup(conf_file_id, 'Rollout')
+
+# Add OneLab as a peer
+onelab = {'peername': u'OneLab', 'peer_url': u'https://onelab-plc.inria.fr/PLCAPI/', 'key': u'-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v1.4.5 (GNU/Linux)\n\nmQGiBEW0kJMRBACaTlrW0eYlQwkzRuMFfEYMwyqBT9Bm6R4g68SJ5GdjCRu3XCnd\nGTGCFF4ewOu6IcUmZDv39eqxShBWyx+JqBogYPGNvPrj07jXXKaSBCM7TPk+9kMW\nPziIxSClvO15XaPKv89c6kFaEBe0z1xsoMB/TNoLmhFUxmc24O7JnEqmYwCgjzIS\nHP7u9KIOYk1ZlTdOtwyRxVkD/1uYbPzD0Qigf8uF9ADzx7I4F1ATd2ezYq0EfzhD\nTDa15FPWwA7jm+Mye//ovT01Ju6JQtCU4N9wRsV2Yy2tWcWFZiYt+BISPVS0lJDx\nQ2Cd2+kEWyl9ByL9/ACHmCUz0OOaz9j1x+GpJLArjUdZSJOs68kPw90F62mrLHfg\nYCHpA/0ZcdJQG9QYNZ67KMFqNPho+uRww5/7kxQ4wkSyP7EK3QUVgXG5OWZ/1mPZ\njon9N04nnjrL9qoQv7m04ih3rmqyGy1MsicNCoys0RNh1eavPdAsXD1ZEXnWPA7z\naC37hxUaRPP3hH+1ifjPpAWQX1E89MK2y2zQpZipvEOAO2Lw8LRCT25lTGFiIENl\nbnRyYWwgKGh0dHA6Ly9vbmVsYWItcGxjLmlucmlhLmZyLykgPHN1cHBvcnRAb25l\nLWxhYi5vcmc+iGAEExECACAFAkW0kJMCGyMGCwkIBwMCBBUCCAMEFgIDAQIeAQIX\ngAAKCRBuu7E0vzFd9fvbAJ9QB2neTSbAN5HuoigIbuKzTUCTjQCeM/3h7/OmjD+z\n6yXtWD4Fzyfr7fSIYAQTEQIAIAUCRbibbAIbIwYLCQgHAwIEFQIIAwQWAgMBAh4B\nAheAAAoJEG67sTS/MV31w3AAn2t6qb94HIPmqCoD/ptK34Dv+VW0AJ4782ffPPnk\nbVXHU/Sx31QCoFmj34hgBBMRAgAgBQJFtJJBAhsjBgsJCAcDAgQVAggDBBYCAwEC\nHgECF4AACgkQbruxNL8xXfU5UQCeKqXWeNzTqdMqj/qHPkp1JCb+isEAn2AzDnde\nITF0aYd02RAKsU4sKePEtEJPbmVMYWIgQ2VudHJhbCAoaHR0cDovL29uZWxhYi1w\nbGMuaW5yaWEuZnIvKSA8c3VwcG9ydEBvbmUtbGFiLm9yZz6IYAQTEQIAIAUCRbi2\npgIbIwYLCQgHAwIEFQIIAwQWAgMBAh4BAheAAAoJEG67sTS/MV31W4AAn0rW5yjR\n2a8jPP/V44gw1JhqnE8jAKCMAEh0nPjvle5oLEGectC3Es9Pm7kBDQRFtJCUEAQA\nhp38fNVy/aJiPg2lUKKnA6KjrRm3LxD66N8MSWfxGCIYzQRJHhmZWnS+m1DDOjdu\nFG9FM6QrsCRRcEQuvhKI2ORFfK75D24lj4QaXzw7vfBbAibTaDsYa0b5LxfR5pGj\nYPCQ5LrRex+Ws3DrB3acJE5/XnYJZ+rUO1ZJlm00FTMAAwUD/Ai4ZUunVB8F0VqS\nhJgDYQF08/OlAnDAcbL//P5dtXdztUNSgXZM4wW/XFnDvAsBuRnbfkT/3BeptM9L\neEbdrMi4eThLstSl13ITOsZbSL3i/2OO9sPAxupWzRWOXcQILpqR2YMRK1EapO+M\nNhjrgxU9JpMXz24FESocczSyywDXiEkEGBECAAkFAkW0kJQCGwwACgkQbruxNL8x\nXfXGxQCfZqzSqinohParWaHv+4XNoIz2B7IAn2Ge0O5wjYZeV/joulkTXfPKm7Iu\n=SsZg\n-----END PGP PUBLIC KEY BLOCK-----\n', 'cacert': u'Certificate:\r\n    Data:\r\n        Version: 3 (0x2)\r\n        Serial Number: 67109883 (0x40003fb)\r\n        Signature Algorithm: sha1WithRSAEncryption\r\n        Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=G\r\n        Validity\r\n            Not Before: Mar 14 20:30:00 2006 GMT\r\n            Not After : Mar 14 23:59:00 2013 GMT\r\n        Subject: C=BE, O=Cybertrust, OU=Educational CA, CN=Cybertrust Educationa\r\n        Subject Public Key Info:\r\n            Public Key Algorithm: rsaEncryption\r\n            RSA Public Key: (2048 bit)\r\n                Modulus (2048 bit):\r\n                    00:95:22:a1:10:1d:4a:46:60:6e:05:91:9b:df:83:\r\n                    c2:ed:12:b2:5a:7c:f8:ab:e1:f8:50:5c:28:2c:7e:\r\n                    7e:00:38:93:b0:8b:4a:f1:c2:4c:3c:10:2c:3c:ef:\r\n                    b0:ec:a1:69:2f:b9:fc:cc:08:14:6b:8d:4f:18:f3:\r\n                    83:d2:fa:a9:37:08:20:aa:5c:aa:80:60:a2:d5:a5:\r\n                    22:00:cf:5a:e5:b4:97:df:ba:1e:be:5c:8e:17:19:\r\n                    66:fd:af:9f:7c:7b:89:b2:0e:24:d8:c7:ab:63:c4:\r\n                    95:32:8d:48:e6:63:59:7d:04:b8:33:a8:bd:d7:5d:\r\n                    64:bc:63:b5:f7:4d:28:fd:f9:06:72:31:5c:ba:45:\r\n                    94:65:a3:d2:b4:58:ec:3b:61:58:44:a3:2f:62:b3:\r\n                    9b:80:b4:82:fd:d5:c7:cc:51:25:e5:95:3f:47:2f:\r\n                    30:7b:ac:c8:78:6e:e2:e1:6d:27:eb:3d:cc:01:82:\r\n                    e8:35:77:8d:ab:58:bb:55:d1:d5:a4:81:56:8d:1c:\r\n                    d0:14:b1:b0:06:de:a0:91:22:f3:f0:a8:34:17:47:\r\n                    c6:e0:3e:f6:0c:5a:ac:7e:50:4b:cd:e1:69:6e:06:\r\n                    fc:06:7e:6a:4d:b4:95:99:a0:59:5c:35:66:ec:d9:\r\n                    49:d4:17:e0:60:b0:5d:a5:d7:1a:e2:2a:6e:66:f2:\r\n                    af:1d\r\n                Exponent: 65537 (0x10001)\r\n        X509v3 extensions:\r\n            X509v3 CRL Distribution Points: \r\n                URI:http://www.public-trust.com/cgi-bin/CRL/2018/cdp.crl\r\n\r\n            X509v3 Subject Key Identifier: \r\n                65:65:A3:3D:D7:3B:11:A3:0A:07:25:37:C9:42:4A:5B:76:77:50:E1\r\n            X509v3 Certificate Policies: \r\n                Policy: 1.3.6.1.4.1.6334.1.0\r\n                  CPS: http://www.public-trust.com/CPS/OmniRoot.html\r\n\r\n            X509v3 Authority Key Identifier: \r\n                DirName:/C=US/O=GTE Corporation/OU=GTE CyberTrust Solutions, Inc\r\n                serial:01:A5\r\n\r\n            X509v3 Key Usage: critical\r\n                Certificate Sign, CRL Sign\r\n            X509v3 Basic Constraints: critical\r\n                CA:TRUE, pathlen:0\r\n    Signature Algorithm: sha1WithRSAEncryption\r\n        43:b3:45:83:54:71:c4:1f:dc:b2:3c:6b:4e:bf:26:f2:4e:f2:\r\n        ad:9a:5b:fa:86:37:88:e8:14:6c:41:18:42:5f:ef:65:3e:eb:\r\n        03:77:a0:b7:9e:75:7a:51:7c:bb:15:5b:b8:af:91:a0:34:92:\r\n        53:ed:7f:2a:49:84:ac:b9:80:4b:b5:c7:b2:23:22:fb:eb:d8:\r\n        fb:6e:c9:3c:f3:d2:d1:bb:be:c9:1c:ff:6d:01:db:69:80:0e:\r\n        99:a5:ea:9e:7b:97:98:8f:b7:cf:22:9c:b3:b8:5d:e5:a9:33:\r\n        17:74:c6:97:37:0f:b4:e9:26:82:5f:61:0b:3f:1e:3d:64:e9:\r\n        2b:9b\r\n-----BEGIN CERTIFICATE-----\r\nMIIEQjCCA6ugAwIBAgIEBAAD+zANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV\r\nUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU\r\ncnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds\r\nb2JhbCBSb290MB4XDTA2MDMxNDIwMzAwMFoXDTEzMDMxNDIzNTkwMFowXzELMAkG\r\nA1UEBhMCQkUxEzARBgNVBAoTCkN5YmVydHJ1c3QxFzAVBgNVBAsTDkVkdWNhdGlv\r\nbmFsIENBMSIwIAYDVQQDExlDeWJlcnRydXN0IEVkdWNhdGlvbmFsIENBMIIBIjAN\r\nBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlSKhEB1KRmBuBZGb34PC7RKyWnz4\r\nq+H4UFwoLH5+ADiTsItK8cJMPBAsPO+w7KFpL7n8zAgUa41PGPOD0vqpNwggqlyq\r\ngGCi1aUiAM9a5bSX37oevlyOFxlm/a+ffHuJsg4k2MerY8SVMo1I5mNZfQS4M6i9\r\n111kvGO1900o/fkGcjFcukWUZaPStFjsO2FYRKMvYrObgLSC/dXHzFEl5ZU/Ry8w\r\ne6zIeG7i4W0n6z3MAYLoNXeNq1i7VdHVpIFWjRzQFLGwBt6gkSLz8Kg0F0fG4D72\r\nDFqsflBLzeFpbgb8Bn5qTbSVmaBZXDVm7NlJ1BfgYLBdpdca4ipuZvKvHQIDAQAB\r\no4IBbzCCAWswRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL3d3dy5wdWJsaWMtdHJ1\r\nc3QuY29tL2NnaS1iaW4vQ1JMLzIwMTgvY2RwLmNybDAdBgNVHQ4EFgQUZWWjPdc7\r\nEaMKByU3yUJKW3Z3UOEwUwYDVR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEF\r\nBQcCARYtaHR0cDovL3d3dy5wdWJsaWMtdHJ1c3QuY29tL0NQUy9PbW5pUm9vdC5o\r\ndG1sMIGJBgNVHSMEgYEwf6F5pHcwdTELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD0dU\r\nRSBDb3Jwb3JhdGlvbjEnMCUGA1UECxMeR1RFIEN5YmVyVHJ1c3QgU29sdXRpb25z\r\nLCBJbmMuMSMwIQYDVQQDExpHVEUgQ3liZXJUcnVzdCBHbG9iYWwgUm9vdIICAaUw\r\nDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwDQYJKoZIhvcNAQEF\r\nBQADgYEAQ7NFg1RxxB/csjxrTr8m8k7yrZpb+oY3iOgUbEEYQl/vZT7rA3egt551\r\nelF8uxVbuK+RoDSSU+1/KkmErLmAS7XHsiMi++vY+27JPPPS0bu+yRz/bQHbaYAO\r\nmaXqnnuXmI+3zyKcs7hd5akzF3TGlzcPtOkmgl9hCz8ePWTpK5s=\r\n-----END CERTIFICATE-----\r\nCertificate:\r\n    Data:\r\n        Version: 1 (0x0)\r\n        Serial Number: 421 (0x1a5)\r\n        Signature Algorithm: md5WithRSAEncryption\r\n        Issuer: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root\r\n        Validity\r\n            Not Before: Aug 13 00:29:00 1998 GMT\r\n            Not After : Aug 13 23:59:00 2018 GMT\r\n        Subject: C=US, O=GTE Corporation, OU=GTE CyberTrust Solutions, Inc., CN=GTE CyberTrust Global Root\r\n        Subject Public Key Info:\r\n            Public Key Algorithm: rsaEncryption\r\n            RSA Public Key: (1024 bit)\r\n                Modulus (1024 bit):\r\n                    00:95:0f:a0:b6:f0:50:9c:e8:7a:c7:88:cd:dd:17:\r\n                    0e:2e:b0:94:d0:1b:3d:0e:f6:94:c0:8a:94:c7:06:\r\n                    c8:90:97:c8:b8:64:1a:7a:7e:6c:3c:53:e1:37:28:\r\n                    73:60:7f:b2:97:53:07:9f:53:f9:6d:58:94:d2:af:\r\n                    8d:6d:88:67:80:e6:ed:b2:95:cf:72:31:ca:a5:1c:\r\n                    72:ba:5c:02:e7:64:42:e7:f9:a9:2c:d6:3a:0d:ac:\r\n                    8d:42:aa:24:01:39:e6:9c:3f:01:85:57:0d:58:87:\r\n                    45:f8:d3:85:aa:93:69:26:85:70:48:80:3f:12:15:\r\n                    c7:79:b4:1f:05:2f:3b:62:99\r\n                Exponent: 65537 (0x10001)\r\n    Signature Algorithm: md5WithRSAEncryption\r\n        6d:eb:1b:09:e9:5e:d9:51:db:67:22:61:a4:2a:3c:48:77:e3:\r\n        a0:7c:a6:de:73:a2:14:03:85:3d:fb:ab:0e:30:c5:83:16:33:\r\n        81:13:08:9e:7b:34:4e:df:40:c8:74:d7:b9:7d:dc:f4:76:55:\r\n        7d:9b:63:54:18:e9:f0:ea:f3:5c:b1:d9:8b:42:1e:b9:c0:95:\r\n        4e:ba:fa:d5:e2:7c:f5:68:61:bf:8e:ec:05:97:5f:5b:b0:d7:\r\n        a3:85:34:c4:24:a7:0d:0f:95:93:ef:cb:94:d8:9e:1f:9d:5c:\r\n        85:6d:c7:aa:ae:4f:1f:22:b5:cd:95:ad:ba:a7:cc:f9:ab:0b:\r\n        7a:7f\r\n-----BEGIN CERTIFICATE-----\r\nMIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD\r\nVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv\r\nbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv\r\nb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV\r\nUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU\r\ncnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds\r\nb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH\r\niM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS\r\nr41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4\r\n04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r\r\nGwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9\r\n3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P\r\nlZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/\r\n-----END CERTIFICATE-----\r\n'}
+
+AddPeer(onelab)
diff --git a/tools/upgrade-db.py b/tools/upgrade-db.py
new file mode 100755 (executable)
index 0000000..b7614f0
--- /dev/null
@@ -0,0 +1,457 @@
+#!/usr/bin/python
+#
+# Tool for upgrading/converting a db
+# Requirements:
+# 1) Databse Schema - schema for the new database you what to upgrade to
+# 2) Config File - the config file that describes how to convert the db
+#
+# Notes:
+# 1) Will attempt to convert the db defined in  /etc/planetlab/plc_config
+# 2) Does not automatically drop archived database. They must be removed
+#    manually
+
+import sys
+import os
+import getopt
+import pgdb
+
+config = {}
+config_file = "/etc/planetlab/plc_config"
+execfile(config_file, config)
+upgrade_config_file = "plcdb.3-4.conf"
+schema_file = "planetlab4.sql"
+temp_dir = "/tmp"
+
+
+def usage():
+        print "Usage: %s [OPTION] UPGRADE_CONFIG_FILE " % sys.argv[0]
+        print "Options:"
+        print "     -s, --schema=FILE       Upgraded Database Schema"
+        print "     -t, --temp-dir=DIR      Temp Directory"
+        print "     --help                  This message"
+        sys.exit(1)
+
+try:
+        (opts, argv) = getopt.getopt(sys.argv[1:],
+                                     "s:d:",
+                                     ["schema=",
+                                      "temp-dir=",
+                                      "help"])
+except getopt.GetoptError, err:
+       print "Error: ", err.msg
+        usage()
+
+for (opt, optval) in opts:
+        if opt == "-s" or opt == "--schema":
+               schema_file = optval
+        elif opt == "-d" or opt == "--temp-dir":
+               temp_dir = optval
+        elif opt == "--help":
+               usage()
+try:
+       upgrade_config_file = argv[0]
+except IndexError:
+       print "Error: too few arguments"
+        usage()
+
+schema = {}
+inserts = []
+schema_items_ordered = []
+sequences = {}
+temp_tables = {}
+
+
+# load conf file for this upgrade
+try:
+        upgrade_config = {}
+        execfile(upgrade_config_file, upgrade_config)
+        upgrade_config.pop('__builtins__')
+       db_version_previous = upgrade_config['DB_VERSION_PREVIOUS']
+        db_version_new = upgrade_config['DB_VERSION_NEW']
+
+except IOError, fault:
+        print "Error: upgrade config file (%s) not found. Exiting" % \
+               (fault)
+        sys.exit(1) 
+except KeyError, fault:
+       print "Error: %s not set in upgrade confing (%s). Exiting" % \
+               (fault, upgrade_config_file)
+       sys.exit(1)
+
+
+
+
+def connect():
+       db = pgdb.connect(user = config['PLC_DB_USER'],
+                 database = config['PLC_DB_NAME'])     
+       return db
+
+def archive_db(database, archived_database):
+
+        archive_db = " dropdb -U postgres %s > /dev/null 2>&1;" \
+                    " psql template1 postgres -qc " \
+                     " 'ALTER DATABASE %s RENAME TO %s;';" % \
+                     (archived_database, database, archived_database)
+       exit_status = os.system(archive_db)
+        if exit_status:
+                print "Error: unable to archive database. Upgrade failed"
+                sys.exit(1)
+        #print "Status: %s has been archived. now named %s" % (database, archived_database)
+
+
+def encode_utf8(inputfile_name, outputfile_name):
+       # rewrite a iso-8859-1 encoded file in utf8
+       try:
+               inputfile = open(inputfile_name, 'r')
+               outputfile = open(outputfile_name, 'w')
+               for line in inputfile:
+                       if line.upper().find('SET CLIENT_ENCODING') > -1:
+                               continue
+                       outputfile.write(unicode(line, 'iso-8859-1').encode('utf8'))
+               inputfile.close()
+               outputfile.close()              
+       except:
+               print 'error encoding file'
+               raise
+
+def create_item_from_schema(item_name):
+
+       try:
+                (type, body_list) = schema[item_name]
+               exit_status = os.system('psql %s %s -qc "%s" > /dev/null 2>&1' % \
+                           (config['PLC_DB_NAME'], config['PLC_DB_USER'],"".join(body_list) ) )
+               if exit_status:
+                       raise Exception
+        except Exception, fault:
+                print 'Error: create %s failed. Check schema.' % item_name
+               sys.exit(1)
+               raise fault
+
+        except KeyError:
+                print "Error: cannot create %s. definition not found in %s" % \
+                        (key, schema_file)
+                return False
+
+def fix_row(row, table_name, table_fields):
+
+       if table_name in ['nodenetworks']:
+               # convert str bwlimit to bps int
+               bwlimit_index = table_fields.index('bwlimit')
+               if isinstance(row[bwlimit_index], int):
+                       pass
+               elif row[bwlimit_index].find('mbit') > -1:
+                       row[bwlimit_index] = int(row[bwlimit_index].split('mbit')[0]) \
+                                           * 1000000
+               elif row[bwlimit_index].find('kbit') > -1:
+                       row[bwlimit_index] = int(row[bwlimit_index].split('kbit')[0]) \
+                                            * 1000
+       elif table_name in ['slice_attribute']:
+               # modify some invalid foreign keys
+               attribute_type_index = table_fields.index('attribute_type_id')
+               if row[attribute_type_index] == 10004:
+                       row[attribute_type_index] = 10016
+               elif row[attribute_type_index] == 10006:
+                       row[attribute_type_index] = 10017
+               elif row[attribute_type_index] in [10031, 10033]:
+                       row[attribute_type_index] = 10037
+               elif row[attribute_type_index] in [10034, 10035]:
+                       row[attribute_type_index] = 10036
+       elif table_name in ['slice_attribute_types']:
+               type_id_index = table_fields.index('attribute_type_id')
+               if row[type_id_index] in [10004, 10006, 10031, 10033, 10034, 10035]:
+                       return None
+       return row
+       
+def fix_table(table, table_name, table_fields):
+       if table_name in ['slice_attribute_types']:
+               # remove duplicate/redundant primary keys
+               type_id_index = table_fields.index('attribute_type_id')
+               for row in table:
+                       if row[type_id_index] in [10004, 10006, 10031, 10033, 10034, 10035]:
+                               table.remove(row)
+       return table
+
+def remove_temp_tables():
+       # remove temp_tables
+       try:
+               for temp_table in temp_tables:
+                       os.remove(temp_tables[temp_table])
+       except:
+               raise
+
+def generate_temp_table(table_name, db):
+       cursor = db.cursor()
+        try:
+                # get upgrade directions
+                table_def = upgrade_config[table_name].replace('(', '').replace(')', '').split(',')
+                table_fields, old_fields, joins, wheres = [], [], set(), set()
+                for field in table_def:
+                        field_parts = field.strip().split(':')
+                        table_fields.append(field_parts[0])
+                        old_fields.append(field_parts[1])
+                        if field_parts[2:]:    
+                               joins.update(set(filter(lambda x: not x.find('=') > -1, field_parts[2:])))
+                               wheres.update(set(filter(lambda x: x.find('=') > -1, field_parts[2:])))
+               
+               # get indices of fields that cannot be null
+               (type, body_list) = schema[table_name]
+               not_null_indices = []
+               for field in table_fields:
+                       for body_line in body_list:
+                               if body_line.find(field) > -1 and \
+                                  body_line.upper().find("NOT NULL") > -1:
+                                       not_null_indices.append(table_fields.index(field))
+               # get index of primary key
+               primary_key_indices = []
+               for body_line in body_list:
+                       if body_line.find("PRIMARY KEY") > -1:
+                               primary_key = body_line
+                               for field in table_fields:
+                                       if primary_key.find(" "+field+" ") > -1:
+                                               primary_key_indices.append(table_fields.index(field))
+                               #break
+       
+                # get old data
+                get_old_data = "SELECT DISTINCT %s FROM %s" % \
+                      (", ".join(old_fields), old_fields[0].split(".")[0])
+                for join in joins:
+                        get_old_data = get_old_data + " INNER JOIN %s USING (%s) " % \
+                                       (join.split('.')[0], join.split('.')[1])
+               if wheres:      
+                       get_old_data = get_old_data + " WHERE " 
+               for where in wheres:
+                       get_old_data = get_old_data + " %s" % where 
+                cursor.execute(get_old_data)
+                rows = cursor.fetchall()
+
+                # write data to a temp file
+                temp_file_name = '%s/%s.tmp' % (temp_dir, table_name)
+                temp_file = open(temp_file_name, 'w')
+                for row in rows:
+                       # attempt to make any necessary fixes to data
+                       row = fix_row(row, table_name, table_fields)
+                       # do not attempt to write null rows
+                       if row == None:
+                               continue
+                       # do not attempt to write rows with null primary keys
+                       if filter(lambda x: row[x] == None, primary_key_indices):
+                               continue 
+                        for i in range(len(row)):
+                               # convert nulls into something pg can understand
+                               if row[i] == None:
+                                        if i in not_null_indices:
+                                               # XX doesnt work if column is int type
+                                               row[i] = ""
+                                       else: 
+                                               row[i] = "\N"
+                                if isinstance(row[i], int) or isinstance(row[i], float):
+                                        row[i] = str(row[i])
+                               # escape whatever can mess up the data format
+                               if isinstance(row[i], str):
+                                       row[i] = row[i].replace('\t', '\\t')
+                                       row[i] = row[i].replace('\n', '\\n')
+                                       row[i] = row[i].replace('\r', '\\r')
+                       data_row = "\t".join(row)
+                       temp_file.write(data_row + "\n")
+                temp_file.write("\.\n")
+                temp_file.close()
+                temp_tables[table_name] = temp_file_name
+
+        except KeyError:
+                #print "WARNING: cannot upgrade %s. upgrade def not found. skipping" % \
+                #       (table_name)
+                return False
+        except IndexError, fault:
+                print "Error: error found in upgrade config file. " \
+                      "check %s configuration. Aborting " % \
+                      (table_name)
+                sys.exit(1)
+        except:
+                print "Error: configuration for %s doesnt match db schema. " \
+                     " Aborting" % (table_name)
+                try:
+                        db.rollback()
+                except:
+                        pass
+               raise
+
+
+# Connect to current db
+db = connect()
+cursor = db.cursor()
+
+# determin current db version
+try:
+       cursor.execute("SELECT relname from pg_class where relname = 'plc_db_version'")
+       rows = cursor.fetchall()
+       if not rows:
+               print "Warning: current db has no version. Unable to validate config file."
+       else:
+               cursor.execute("SELECT version FROM plc_db_version")
+               rows = cursor.fetchall()
+               if not rows or not rows[0]:
+                       print "Warning: current db has no version. Unable to validate config file."
+               elif rows[0][0] == db_version_new:
+                               print "Status: Versions are the same. No upgrade necessary."
+                       sys.exit()
+               elif not rows[0][0] == db_version_previous:
+                       print "Stauts: DB_VERSION_PREVIOUS in config file (%s) does not" \
+                             " match current db version %d" % (upgrade_config_file, rows[0][0])
+                       sys.exit()
+               else:
+                       print "STATUS: attempting upgrade from %d to %d" % \
+                                (db_version_previous, db_version_new)  
+       
+       # check db encoding
+       sql = " SELECT pg_catalog.pg_encoding_to_char(d.encoding)" \
+             " FROM pg_catalog.pg_database d " \
+             " WHERE d.datname = '%s' " % config['PLC_DB_NAME']
+       cursor.execute(sql)
+       rows = cursor.fetchall()
+       if rows[0][0] not in ['UTF8', 'UNICODE']:
+               print "WARNING: db encoding is not utf8. Attempting to encode"
+               db.close()
+               # generate db dump
+               dump_file = '%s/dump.sql' % (temp_dir)
+               dump_file_encoded = dump_file + ".utf8"
+               dump_cmd = 'pg_dump -i %s -U postgres -f %s > /dev/null 2>&1' % \
+                          (config['PLC_DB_NAME'], dump_file)
+               if os.system(dump_cmd):
+                       print "ERROR: during db dump. Exiting."
+                       sys.exit(1)
+               # encode dump to utf8
+               print "Status: encoding database dump"
+               encode_utf8(dump_file, dump_file_encoded)
+               # archive original db
+               archive_db(config['PLC_DB_NAME'], config['PLC_DB_NAME']+'_sqlascii_archived')
+               # create a utf8 database and upload encoded data
+               recreate_cmd = 'createdb -U postgres -E UTF8 %s > /dev/null; ' \
+                              'psql -a -U  %s %s < %s > /dev/null 2>&1;'   % \
+                         (config['PLC_DB_NAME'], config['PLC_DB_USER'], \
+                          config['PLC_DB_NAME'], dump_file_encoded) 
+               print "Status: recreating database as utf8"
+               if os.system(recreate_cmd):
+                       print "Error: database encoding failed. Aborting"
+                       sys.exit(1)
+               
+               os.remove(dump_file_encoded)
+               os.remove(dump_file)
+except:
+       raise
+
+
+db = connect()
+cursor = db.cursor()
+
+# parse the schema user wishes to upgrade to
+try:
+       file = open(schema_file, 'r')
+       index = 0
+       lines = file.readlines()
+       while index < len(lines):
+               line = lines[index] 
+               if line.find("--") > -1:
+                       line_parts = line.split("--")
+                        line = line_parts[0]
+               # find all created objects
+               if line.startswith("CREATE"):
+                       line_parts = line.split(" ")
+                       if line_parts[1:3] == ['OR', 'REPLACE']:
+                               line_parts = line_parts[2:]
+                       item_type = line_parts[1]
+                       item_name = line_parts[2]
+                       schema_items_ordered.append(item_name)
+                       if item_type in ['INDEX']:
+                               schema[item_name] = (item_type, line)
+                       
+                       # functions, tables, views span over multiple lines
+                       # handle differently than indexes
+                       elif item_type in ['AGGREGATE', 'TABLE', 'VIEW']:
+                               fields = [line]
+                               while index < len(lines):
+                                       index = index + 1
+                                       nextline =lines[index]
+                                       if nextline.find("--") > -1:
+                                                new_line_parts = nextline.split("--")
+                                                nextline = new_line_parts[0]
+                                       # look for any sequences
+                                       if item_type in ['TABLE'] and nextline.find('serial') > -1:
+                                               sequences[item_name] = nextline.strip().split()[0]
+                                       fields.append(nextline)
+                                       if nextline.find(";") >= 0:
+                                               break
+                               schema[item_name] = (item_type, fields)
+                       else:
+                               print "Error: unknown type %s" % item_type
+               elif line.startswith("INSERT"):
+                       inserts.append(line)
+               index = index + 1
+                               
+except:
+       raise
+
+print "Status: generating temp tables"
+# generate all temp tables
+for key in schema_items_ordered:
+       (type, body_list) = schema[key]
+       if type == 'TABLE':
+               generate_temp_table(key, db)
+
+# disconenct from current database and archive it
+cursor.close()
+db.close()
+
+print "Status: archiving database"
+archive_db(config['PLC_DB_NAME'], config['PLC_DB_NAME']+'_archived')
+os.system('createdb -U postgres -E UTF8 %s > /dev/null; ' % config['PLC_DB_NAME'])
+
+print "Status: upgrading database"
+# attempt to create and load all items from schema into temp db
+try:
+       for key in schema_items_ordered:
+               (type, body_list) = schema[key]
+               create_item_from_schema(key)
+               if type == 'TABLE':
+                       if upgrade_config.has_key(key):                         
+                               # attempt to populate with temp table data
+                               table_def = upgrade_config[key].replace('(', '').replace(')', '').split(',')
+                               table_fields = [field.strip().split(':')[0] for field in table_def]
+                               insert_cmd = "psql %s %s -c " \
+                                             " 'COPY %s (%s) FROM stdin;' < %s " % \
+                                             (config['PLC_DB_NAME'], config['PLC_DB_USER'], key, 
+                                             ", ".join(table_fields), temp_tables[key] )
+                               exit_status = os.system(insert_cmd)
+                               if exit_status:
+                                       print "Error: upgrade %s failed" % key
+                                       sys.exit(1)
+                               # update the primary key sequence
+                               if sequences.has_key(key):
+                                       sequence = key +"_"+ sequences[key] +"_seq"
+                                       update_seq = "psql %s %s -c " \
+                                            " \"select setval('%s', max(%s)) FROM %s;\" > /dev/null" % \
+                                            (config['PLC_DB_NAME'], config['PLC_DB_USER'], sequence, 
+                                             sequences[key], key)
+                                       exit_status = os.system(update_seq)
+                                       if exit_status:
+                                               print "Error: sequence %s update failed" % sequence
+                                               sys.exit(1)
+                       else:
+                               # check if there are any insert stmts in schema for this table
+                               print "Warning: %s has no temp data file. Unable to populate with old data" % key
+                               for insert_stmt in inserts:
+                                       if insert_stmt.find(key) > -1:
+                                               insert_cmd = 'psql %s postgres -qc "%s;" > /dev/null 2>&1' % \
+                                               (config['PLC_DB_NAME'], insert_stmt)
+                                               os.system(insert_cmd) 
+except:
+       print "Error: failed to populate db. Unarchiving original database and aborting"
+       undo_command = "dropdb -U postgres %s > /dev/null; psql template1 postgres -qc" \
+                       " 'ALTER DATABASE %s RENAME TO %s;';  > /dev/null" % \
+                       (config['PLC_DB_NAME'], config['PLC_DB_NAME']+'_archived', config['PLC_DB_NAME'])
+       os.system(undo_command) 
+       #remove_temp_tables()
+       raise
+       
+#remove_temp_tables()
+
+print "upgrade complete"