split plc.d/ and db-config.d between myplc and plcapi modules as a first step
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Tue, 19 Jan 2010 11:55:09 +0000 (11:55 +0000)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Tue, 19 Jan 2010 11:55:09 +0000 (11:55 +0000)
conf_files would move to nodeconfig
some other stuff ready to move to the appropriat module as well

14 files changed:
PLCAPI.spec
db-config.d/000-functions [new file with mode: 0644]
db-config.d/001-admin_user [new file with mode: 0644]
db-config.d/002-system_site [new file with mode: 0644]
db-config.d/010-slice_tags [new file with mode: 0644]
db-config.d/011-node_tags [new file with mode: 0644]
db-config.d/012-interface_tags [new file with mode: 0644]
db-config.d/013-wireless_tags [new file with mode: 0644]
db-config.d/020-boot_states [new file with mode: 0644]
db-config.d/050-pcu_types [new file with mode: 0644]
db-config.d/060-messages [new file with mode: 0644]
plc.d/api [new file with mode: 0755]
plc.d/db [new file with mode: 0755]
plc.d/postgresql [new file with mode: 0755]

index 0b2cdd9..39066fa 100644 (file)
@@ -37,6 +37,10 @@ Requires: mod_python
 Requires: mod_ssl
 Requires: SOAPpy
 
+### avoid having yum complain about updates, as stuff is moving around
+# plc.d/api
+Conflicts: MyPLC <= 4.3-37
+
 # We use psycopg2
 BuildRequires: postgresql-devel
 
@@ -85,6 +89,17 @@ cat > $RPM_BUILD_ROOT/%{_sysconfdir}/php.d/xmlrpc.ini <<EOF
 extension=xmlrpc.so
 EOF
 
+# Install initscripts
+echo "* Installing initscripts"
+find plc.d | cpio -p -d -u ${RPM_BUILD_ROOT}/etc/
+chmod 755 ${RPM_BUILD_ROOT}/etc/plc.d/*
+
+# Install db-config.d files
+echo "* Installing db-config.d files"
+mkdir -p ${RPM_BUILD_ROOT}/etc/planetlab/db-config.d
+cp db-config.d/* ${RPM_BUILD_ROOT}/etc/planetlab/db-config.d
+chmod 444 ${RPM_BUILD_ROOT}/etc/planetlab/db-config.d/*
+
 %clean
 rm -rf $RPM_BUILD_ROOT
 
@@ -103,7 +118,7 @@ rm -rf $RPM_BUILD_ROOT
 %changelog
 * Sat Jan 09 2010 Thierry Parmentelat <thierry.parmentelat@sophia.inria.fr> - PLCAPI-4.3-32
 - support for fedora 12
-- fix subtle bug in filering with ] and quotes
+- fix subtle bug in filtering with ] and quotes
 
 * Fri Dec 18 2009 Baris Metin <Talip-Baris.Metin@sophia.inria.fr> - PLCAPI-4.3-31
 - * patch for php-5.3 (the one in f12)
diff --git a/db-config.d/000-functions b/db-config.d/000-functions
new file mode 100644 (file)
index 0000000..70fa078
--- /dev/null
@@ -0,0 +1,144 @@
+# -*-python-*-
+# $Id$
+# $URL$
+#################### 
+import sys, os
+import resource
+
+g_url = ""
+def GetMyPLCURL(): return g_url
+def SetMyPLCURL(url):
+    global g_url
+    g_url = url
+
+# Get all currently registered roles
+g_role_names = [ role['name'] for role in GetRoles()]
+g_role_names.sort()
+
+def SetRole(level, role):
+    global g_role_names
+    if role not in g_role_names:
+        AddRole(level, role)
+        g_role_names.append(role)
+        g_role_names.sort()
+
+# Get list of existing tag types
+g_known_tag_types = [tag_type['tagname'] for tag_type in GetTagTypes()]
+g_known_tag_types.sort()
+
+def SetTagType(tag_type):
+    global g_known_tag_types
+    # Create/update default slice tag types
+    if tag_type['tagname'] not in g_known_tag_types:
+        AddTagType(tag_type)
+        g_known_tag_types.append(tag_type['tagname'])
+        g_known_tag_types.sort()
+    else:
+        UpdateTagType(tag_type['tagname'], tag_type)
+
+# Get list of existing (enabled, global) files
+g_conf_files = GetConfFiles()
+g_conf_files = filter(lambda conf_file: conf_file['enabled'] and \
+                    not conf_file['node_ids'] and \
+                    not conf_file['nodegroup_ids'],
+                    g_conf_files)
+g_dests = [conf_file['dest'] for conf_file in g_conf_files]
+g_conf_files = dict(zip(g_dests, g_conf_files))
+
+# Get list of existing initscripts
+g_oldinitscripts = GetInitScripts()
+g_oldinitscript_names = [script['name'] for script in g_oldinitscripts]
+g_oldinitscripts = dict(zip(g_oldinitscript_names, g_oldinitscripts))
+
+def SetInitScript(initscript):
+    global g_oldinitscripts, g_oldinitscript_names
+    if initscript['name'] not in g_oldinitscript_names:
+        initscript_id = AddInitScript(initscript)
+        g_oldinitscript_names.append(initscript['name'])
+        initscript['initscript_id']=initscript_id
+        g_oldinitscripts[initscript['name']]=initscript
+    else:
+        orig_initscript = g_oldinitscripts[initscript['name']]
+        initscript_id = orig_initscript['initscript_id']
+        UpdateInitScript(initscript_id, initscript)
+        
+def SetConfFile(conf_file):
+    global g_conf_files, g_dests
+    if conf_file['dest'] not in g_dests:
+        AddConfFile(conf_file)
+    else:
+        orig_conf_file = g_conf_files[conf_file['dest']]
+        conf_file_id = orig_conf_file['conf_file_id']
+        UpdateConfFile(conf_file_id, conf_file)
+
+def SetSlice(slice, tags):
+    # Create or Update slice
+    slice_name = slice['name']
+    slices = GetSlices([slice_name])
+    if len(slices)==1:
+        slice_id = slices[0]['slice_id']
+        if slice.has_key('name'):
+            del slice['name']
+        UpdateSlice(slice_id, slice)
+        slice['name']=slice_name
+    else:
+        expires = None
+        if slice.has_key('expires'):
+            expires = slice['expires']
+            del slice['expires']
+        slice_id = AddSlice(slice)
+        if expires <> None:
+            UpdateSlice(slice_id, {'expires':expires})
+
+    # Get slice structure with all fields
+    slice = GetSlices([slice_name])[0]
+
+    # Create/delete all tags
+    # NOTE: update is not needed, since unspecified tags are deleted, 
+    #       and new tags are added
+    slice_tags = []
+    if slice['slice_tag_ids']:
+        # Delete unknown attributes
+        for slice_tag in GetSliceTags(slice['slice_tag_ids']):
+            # ignore sliver tags, as those are custom/run-time values
+            if slice_tag['node_id'] <> None: continue
+            if (slice_tag['tagname'], slice_tag['value']) not in tags:
+                DeleteSliceTag(slice_tag['slice_tag_id'])
+            else:
+                slice_tags.append((slice_tag['tagname'],slice_tag['value']))
+
+    # only add slice tags that are new
+    for (name, value) in tags:
+        if (name,value) not in slice_tags:
+            AddSliceTag(slice_name, name, value)            
+        else:
+            # NOTE: this confirms that the user-specified tag is 
+            #       returned by GetSliceTags
+            pass
+
+def SetMessage(message):
+    messages = GetMessages([message['message_id']])
+    if len(messages)==0:
+        AddMessage(message)
+    else:
+        UpdateMessage(message['message_id'],message)
+
+# Get all model names
+g_pcu_models = [type['model'] for type in GetPCUTypes()]
+
+def SetPCUType(pcu_type):
+    global g_pcu_models
+    if 'pcu_protocol_types' in pcu_type:
+        protocol_types = pcu_type['pcu_protocol_types']
+        # Take this value out of the struct.
+        del pcu_type['pcu_protocol_types']
+    else:
+        protocol_types = []
+
+    if pcu_type['model'] not in g_pcu_models:
+        # Add the name/model info into DB
+        id = AddPCUType(pcu_type)
+        # for each protocol, also add this.
+        for ptype in protocol_types:
+            AddPCUProtocolType(id, ptype)
+
diff --git a/db-config.d/001-admin_user b/db-config.d/001-admin_user
new file mode 100644 (file)
index 0000000..8797164
--- /dev/null
@@ -0,0 +1,24 @@
+# -*-python-*-
+# $Id$
+# $URL$
+#################### 
+# Create/update the default administrator account (should be person_id 2).
+
+admin = { 'person_id': 2,
+          'first_name': "Default",
+          'last_name': "Administrator",
+          'email': plc['root_user'],
+          'password': plc['root_password'] }
+persons = GetPersons([admin['person_id']])
+if not persons:
+    person_id = AddPerson(admin)
+    if person_id != admin['person_id']:
+        # Huh? Someone deleted the account manually from the database.
+        DeletePerson(person_id)
+        raise Exception, "Someone deleted the \"%s %s\" account from the database!" % \
+              (admin['first_name'], admin['last_name'])
+    UpdatePerson(person_id, { 'enabled': True })
+else:
+    person_id = persons[0]['person_id']
+    UpdatePerson(person_id, admin)
+
diff --git a/db-config.d/002-system_site b/db-config.d/002-system_site
new file mode 100644 (file)
index 0000000..ced75a7
--- /dev/null
@@ -0,0 +1,69 @@
+# -*-python-*-
+# $Id$
+# $URL$
+#################### 
+# Create/update and populate the default site (should be site_id 1)
+
+### plc_www holds the contents of the PLC_WWW configuration category
+if plc_www['port'] == '80':
+    url = "http://" + plc_www['host'] + "/"
+elif plc_www['port'] == '443':
+    url = "https://" + plc_www['host'] + "/"
+else:
+    url = "http://" + plc_www['host'] + ":" + plc_www['port'] + "/"
+
+SetMyPLCURL(url)
+
+site = { 'site_id': 1,
+         'name': plc['name'] + " Central",
+         'abbreviated_name': plc['name'],
+         'login_base': plc['slice_prefix'],
+         'is_public': False,
+         'url': url,
+         'max_slices': 100 }
+
+sites = GetSites([site['site_id']])
+if not sites:
+    site_id = AddSite(site['name'], site['abbreviated_name'], site['login_base'], site)
+    if site_id != site['site_id']:
+        DeleteSite(site_id)
+        raise Exception, "Someone deleted the \"%s\" site from the database!" % \
+              site['name']
+    sites = [site]
+
+# Must call UpdateSite() even after AddSite() to update max_slices
+site_id = sites[0]['site_id']
+UpdateSite(site_id, site)
+
+# The default administrator account must be associated with a site
+# in order to login.
+AddPersonToSite(admin['person_id'], site['site_id'])
+SetPersonPrimarySite(admin['person_id'], site['site_id'])
+
+# Grant admin and PI roles to the default administrator account
+AddRoleToPerson(10, admin['person_id'])
+AddRoleToPerson(20, admin['person_id'])
+
+# Associate root ssh key with the default administrator
+keyfile=plc['root_ssh_key_pub']
+person = GetPersons(admin['person_id'])[0]
+keys = GetKeys(person['key_ids'])
+if os.path.exists(keyfile):
+    sshkeyfp = file(keyfile,"r")
+    sshkey = sshkeyfp.read()
+    sshkeyfp.close()
+
+    found=False
+    for key in keys:
+        if key['key_type']=='ssh':
+            if key['key'] == sshkey:
+                found=True
+            else:
+                # should we delete other keys?
+                pass
+    if not found:
+        key_id = AddPersonKey(admin['person_id'],{'key_type':'ssh','key':sshkey})
+else:
+    if len(keys)==0:
+        print "WARNING: default administrator does not have an ssh key"
+        print "and the default ssh root pub key (%s) file does not exist." % keyfile
diff --git a/db-config.d/010-slice_tags b/db-config.d/010-slice_tags
new file mode 100644 (file)
index 0000000..282bc0c
--- /dev/null
@@ -0,0 +1,167 @@
+# -*-python-*-
+# $Id$
+# $URL$
+#################### slice tag types
+# xxx this should move to PLC/Accessors
+
+# Setup default slice tag types
+slicetag_types = \
+[
+    # Slice type (only vserver is supported)
+    {'tagname': "type",
+     'description': "Type of slice (e.g. vserver)",
+     'category' : 'slice/general',
+     'min_role_id': 20},
+    
+    # System slice
+    {'tagname': "system",
+     'description': "Is a default system slice (1) or not (0 or unset)",
+     'category' : 'slice/general',
+     'min_role_id': 10},
+    
+    # Slice enabled (1) or suspended (0)
+    {'tagname': "enabled",
+     'description': "Slice enabled (1 or unset) or suspended (0)",
+     'category' : 'slice/general',
+     'min_role_id': 10},
+    
+    # Slice reference image
+    {'tagname': "vref",
+     'description': "Reference image",
+     'category' : 'slice/config',
+     'min_role_id': 30},
+    
+    # Slice initialization script
+    {'tagname': "initscript",
+     'description': "Slice initialization script",
+     'category' : 'slice/config',
+     'min_role_id': 30},
+    
+    # IP Addresses for a Slice
+    {'tagname': "ip_addresses",
+     'description': "Add an ip address to a slice/sliver.",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    
+    # CPU share
+    {'tagname': "cpu_pct",
+     'description': "Reserved CPU percent",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "cpu_share",
+     'description': "Number of CPU shares",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    
+    # Bandwidth limits
+    {'tagname': "net_min_rate",
+     'description': "Minimum bandwidth (kbps)",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_max_rate",
+     'description': "Maximum bandwidth (kbps)",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_i2_min_rate",
+     'description': "Minimum bandwidth over I2 routes (kbps)",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_i2_max_rate",
+     'description': "Maximum bandwidth over I2 routes (kbps)",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_max_kbyte",
+     'description': "Maximum daily network Tx KByte limit.",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_thresh_kbyte",
+     'description': "KByte limit before warning and throttling.",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_i2_max_kbyte",
+     'description': "Maximum daily network Tx KByte limit to I2 hosts.",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_i2_thresh_kbyte",
+     'description': "KByte limit to I2 hosts before warning and throttling.",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_share",
+     'description': "Number of bandwidth shares",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    {'tagname': "net_i2_share",
+     'description': "Number of bandwidth shares over I2 routes",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    
+    # Disk quota
+    {'tagname': "disk_max",
+     'description': "Disk quota (1k disk blocks)",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    
+    # Proper operations
+    {'tagname': "proper_op",
+     'description': "Proper operation (e.g. bind_socket)",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    
+    # VServer capabilities 
+    {'tagname': "capabilities",
+     'description': "VServer bcapabilities (separate by commas)",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    
+    # Vsys
+    {'tagname': "vsys",
+     'description': "Bind vsys script fd's to a slice's vsys directory.",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    
+    # CoDemux
+    {'tagname': "codemux",
+     'description': "Demux HTTP between slices using localhost ports. Value in the form 'host, localhost port'.",
+     'category' : 'slice/rspec',
+     'min_role_id': 10},
+    
+    # Delegation
+    {'tagname': "delegations",
+     'description': "Coma seperated list of slices to give delegation authority to.",
+     'category' : 'slice/rspec',
+     'min_role_id': 30},
+
+    # Security capability to empower a slice to make an authenticated API call, set by silverauth NM plugin.
+    {'tagname': "hmac",
+     'description': "Sliver authorization key.",
+     'category' : 'slice/auth',
+     'min_role_id': 20},
+
+    # Capability to give a sliver access to unused raw disk
+    {'tagname': "rawdisk",
+     'description': "map unused raw disk devices into the slice",
+     'category' : 'slice/access', # we should get rid of this category thing
+     'min_role_id': 20},
+
+    { 'tagname' : 'exempt_slice_until',
+      'description' : 'Exclude this slice from MyOps until given date (YYYYMMDD)',
+      'category' : 'slice/myops', 
+      'min_role_id' : 10} ,
+]
+
+# add in the platform supported rlimits to the default_attribute_types
+for entry in resource.__dict__.keys() + ["VLIMIT_OPENFD"]:
+    if entry.find("LIMIT_")==1:
+        rlim = entry[len("RLIMIT_"):]
+        rlim = rlim.lower()
+        for ty in ("min","soft","hard"):
+            attribute = {
+                'tagname': "%s_%s"%(rlim,ty),
+                'description': "Per sliver RLIMIT %s_%s."%(rlim,ty),
+                'category': 'slice/limit',
+                'min_role_id': 10 #admin
+                }
+            slicetag_types.append(attribute)
+
+for slicetag_type in slicetag_types:
+    SetTagType(slicetag_type)
diff --git a/db-config.d/011-node_tags b/db-config.d/011-node_tags
new file mode 100644 (file)
index 0000000..3d03521
--- /dev/null
@@ -0,0 +1,21 @@
+# -*-python-*-
+# $Id$
+# $URL$
+#################### node tags
+default_node_tags = [
+    { 'tagname' : 'arch',
+      'description' : 'architecture name',
+      'category' : 'node/config', 
+      'min_role_id' : 40} ,
+    { 'tagname' : 'pldistro',
+      'description' : 'PlanetLab distribution',
+      'category' : 'node/config', 
+      'min_role_id' : 10} ,
+    { 'tagname' : 'deployment',
+      'description' : 'typically "alpha", "beta", or "production"',
+      'category' : 'node/operation', 
+      'min_role_id' : 10} ,
+
+    ]
+
+for node_type in default_node_tags: SetTagType (node_type)
diff --git a/db-config.d/012-interface_tags b/db-config.d/012-interface_tags
new file mode 100644 (file)
index 0000000..e184c39
--- /dev/null
@@ -0,0 +1,26 @@
+# -*-python-*-
+# $Id$
+# $URL$
+#################### interface settings
+# xxx this should move to PLC/Accessors
+
+default_interface_types = \
+[
+    {'category' : "interface/general",
+     'tagname' : "ifname",
+     'description': "Set interface name, instead of eth0 or the like",
+     'min_role_id' : 40},
+
+    {'category' : "interface/multihome",
+     'tagname' : "alias",
+     'description': "Specifies that the network is used for multihoming",
+     'min_role_id' : 40},
+    
+    {'category' : "interface/hidden",
+     'tagname' : "backdoor",
+     'description': "For testing new settings",
+     'min_role_id' : 10},
+]
+
+for setting_type in default_interface_types:
+    SetTagType(setting_type)
diff --git a/db-config.d/013-wireless_tags b/db-config.d/013-wireless_tags
new file mode 100644 (file)
index 0000000..77c6683
--- /dev/null
@@ -0,0 +1,48 @@
+# -*-python-*-
+# $Id: 012-interface_tags -1   $
+# $URL: svn+ssh://thierry@svn.planet-lab.org/svn/MyPLC/trunk/db-config.d/012-interface_tags $
+#################### interface settings
+# xxx this should move to PLC/Accessors
+
+wireless_interface_types = [
+
+    { 'category' : "interface/wifi",
+      'tagname' : x,
+      'description' : "802.11 %s -- see %s"%(y,z),
+      'min_role_id' : 40 } for (x,y,z) in [
+
+    ("mode","Mode","iwconfig"),
+
+    ("essid","ESSID","iwconfig"),
+
+    ("nw","Network Id","iwconfig"),
+
+    ("freq","Frequency","iwconfig"),
+
+    ("channel","Channel","iwconfig"),
+
+    ("sens","sensitivity threshold","iwconfig"),
+
+    ("rate","Rate","iwconfig"),
+
+    ("key","key","iwconfig key"),
+
+    ("key1","key1","iwconfig key [1]"),
+
+    ("key2","key2","iwconfig key [2]"),
+
+    ("key3","key3","iwconfig key [3]"),
+
+    ("key4","key4","iwconfig key [4]"),
+
+    ("securitymode","Security mode","iwconfig enc"),
+
+    ("iwconfig","Additional parameters to iwconfig","ifup-wireless"),
+
+    ("iwpriv","Additional parameters to iwpriv","ifup-wireless"),
+
+    ]
+    ]
+
+for setting_type in wireless_interface_types:
+    SetTagType(setting_type)
diff --git a/db-config.d/020-boot_states b/db-config.d/020-boot_states
new file mode 100644 (file)
index 0000000..b7a8a5b
--- /dev/null
@@ -0,0 +1,23 @@
+# -*-python-*-
+# $Id: 012-slice_tags -1   $
+# $URL: svn+ssh://thierry@svn.planet-lab.org/svn/MyPLC/trunk/db-config.d/012-slice_tags $
+#################### slice tag types
+default_boot_states = [
+    'boot',
+    'failboot',
+    'safeboot',
+    'install',
+    'reinstall',
+    'disabled',
+]
+current_boot_states = GetBootStates()
+for state in default_boot_states:
+    if state not in current_boot_states:
+        AddBootState(state)
+
+# TODO: Delete old boot states. 
+if False:# NOTE: Only set to true if all federating peers have the new default boot states above.
+    for state in current_boot_states:
+        if state not in default_boot_states:
+            DeleteBootState(state)
+    
diff --git a/db-config.d/050-pcu_types b/db-config.d/050-pcu_types
new file mode 100644 (file)
index 0000000..ef53713
--- /dev/null
@@ -0,0 +1,61 @@
+#################### PCUs
+### Setup Initial PCU information
+       
+pcu_types = [
+    {'model': 'HPiLO',
+     'name': 'HP iLO v1 or v2 (Integrated Lights-Out)', },
+    
+    {'model': 'IntelAMT',
+     'name': 'Intel AMT v2.5 or v3.0 (Active Management Technology)', },
+    
+    {'model': 'DRAC',
+     'name': 'DRAC - Dell Remote Access Control (all versions)', },
+    
+    {'model': 'OpenIPMI',
+     'name': 'OpenIPMI - Intelligent Platform Management Interface', },
+    
+    {'model': 'APCControl12p3',
+     'name': 'APC AP79xx or Masterswitch (sequence 1-2-port-3)', },
+    {'model': 'APCControl1p4',
+     'name': 'APC AP79xx or Masterswitch (sequence 1-port-4)', },
+    {'model': 'APCControl121p3',
+     'name': 'APC AP79xx or Masterswitch (sequence 1-2-1-port-3)', },
+    {'model': 'APCControl121p1',
+     'name': 'APC AP79xx or Masterswitch (sequence 1-2-1-port-1)', },
+    {'model': 'APCControl13p13',
+     'name': 'APC AP79xx or Masterswitch (sequence 1-3-port-1-3)', },
+    
+    {'model': 'BayTechRPC3NC', 
+     'name': 'BayTech with prompt RPC3-NC>', },
+    {'model': 'BayTechRPC16', 
+     'name': 'BayTech with prompt RPC-16>', },
+    {'model': 'BayTech',
+     'name': 'BayTech with prompt DS-RPC>', },
+    {'model': 'BayTechCtrlC', 
+     'name': 'BayTech Ctrl-C, 5, then with prompt DS-RPC>', },
+    {'model': 'BayTechCtrlCUnibe', 
+     'name': 'BayTech Ctrl-C, 3, then with prompt DS-RPC>', },
+    
+    {'model': 'BlackBoxPSMaverick',
+     'name': 'BlackBoxPSMaverick Web based controller'},
+    
+    {'model': 'IPAL', 
+     'name': 'IPAL - Dataprobe IP-41x & IP-81x', },
+    {'model': 'ePowerSwitchNew',
+     'name': 'ePowerSwitch Newer Models 1/4/8x', },
+    {'model': 'ePowerSwitchOld',
+     'name': 'ePowerSwitch Older Models 1/4/8x', },
+    
+    {'model': 'PM211MIP',
+     'name': 'Infratec PM221-MIP', },
+    
+    {'model': 'WTIIPS4',
+     'name': 'Western Telematic (WTI IPS-4)', },
+    
+    {'model': 'ManualPCU',
+     'name': 'Manual Administrator Operation (choose if model unknown)', },
+    ]
+
+for pcu_type in pcu_types:
+    SetPCUType(pcu_type)
+
diff --git a/db-config.d/060-messages b/db-config.d/060-messages
new file mode 100644 (file)
index 0000000..78eaa71
--- /dev/null
@@ -0,0 +1,293 @@
+# -*-python-*-
+# $Id$
+# $URL$
+#################### body for messages
+
+installfailed = """Once the node meets these requirements, please reinitiate the install
+by visiting:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+Update the BootState to 'Reinstall', then reboot the node.
+
+If you have already performed this step and are still receiving this
+message, please reply so that we may investigate the problem.
+"""
+
+# Load default message templates
+message_templates = [
+    {'message_id': 'Verify account',
+     'subject': "Verify account registration",
+     'template': """
+Please verify that you registered for a %(PLC_NAME)s account with the
+username %(email)s by visiting:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/persons/register.php?id=%(person_id)d&key=%(verification_key)s
+
+You must wait for this account to be approved before you can begin using it, please be patient.
+
+If you did not register for a %(PLC_NAME)s account, please ignore this
+message, or contact %(PLC_NAME)s Support <%(PLC_MAIL_SUPPORT_ADDRESS)s>.
+"""
+     },
+
+    {'message_id': 'New PI account',
+     'subject': "New PI account registration from %(first_name)s %(last_name)s <%(email)s> at %(site_name)s",
+     'template': """
+%(first_name)s %(last_name)s <%(email)s> has signed up for a new
+%(PLC_NAME)s account at %(site_name)s and has requested a PI role. PIs
+are responsible for enabling user accounts, creating slices, and
+ensuring that all users abide by the %(PLC_NAME)s Acceptable Use
+Policy.
+
+Only %(PLC_NAME)s administrators may enable new PI accounts. If you
+are a PI at %(site_name)s, please respond and indicate whether this
+registration is acceptable.
+
+To view the request, visit:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/persons/index.php?id=%(person_id)d
+"""
+     },
+
+    {'message_id': 'New account',
+     'subject': "New account registration from %(first_name)s %(last_name)s <%(email)s> at %(site_name)s",
+     'template': """
+%(first_name)s %(last_name)s <%(email)s> has signed up for a new
+%(PLC_NAME)s account at %(site_name)s and has requested the following
+roles: %(roles)s.
+
+To deny the request or enable the account, visit:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/persons/index.php?id=%(person_id)d
+"""
+     },
+
+    {'message_id': 'Password reset requested',
+     'subject': "Password reset requested",
+     'template': """
+Someone has requested that the password of your %(PLC_NAME)s account
+%(email)s be reset. If this person was you, you may continue with the
+reset by visiting:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/persons/reset_password.php?id=%(person_id)d&key=%(verification_key)s
+
+If you did not request that your password be reset, please contact
+%(PLC_NAME)s Support <%(PLC_MAIL_SUPPORT_ADDRESS)s>. Do not quote or
+otherwise include any of this text in any correspondence.
+"""
+     },
+
+    {'message_id': 'Password reset',
+     'subject': "Password reset",
+     'template': """
+The password of your %(PLC_NAME)s account %(email)s has been
+temporarily reset to:
+
+%(password)s
+
+Please change it at as soon as possible by visiting:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/persons/index.php?id=%(person_id)d
+
+If you did not request that your password be reset, please contact
+%(PLC_NAME)s Support <%(PLC_MAIL_SUPPORT_ADDRESS)s>. Do not quote or
+otherwise include any of this text in any correspondence.
+"""
+     },
+
+    # Boot Manager messages
+    {'message_id': "installfinished",
+     'subject': "%(hostname)s completed installation",
+     'template': """
+%(hostname)s just completed installation.
+
+The node should be usable in a couple of minutes if installation was
+successful.
+"""
+     },
+
+    {'message_id': "insufficientdisk",
+     'subject': "%(hostname)s does not have sufficient disk space",
+     'template': """
+%(hostname)s failed to boot because it does not have sufficent disk
+space, or because its disk controller was not recognized.
+
+Please replace the current disk or disk controller or install
+additional disks to meet the current hardware requirements.
+""" + installfailed
+     },
+
+    {'message_id': "insufficientmemory",
+     'subject': "%(hostname)s does not have sufficient memory",
+     'template': """
+%(hostname)s failed to boot because it does not have sufficent
+memory.
+
+Please install additional memory to meet the current hardware
+requirements.
+""" + installfailed
+     },
+
+    {'message_id': "authfail",
+     'subject': "%(hostname)s failed to authenticate",
+     'template':
+"""
+%(hostname)s failed to authenticate for the following reason:
+
+%(fault)s
+
+The most common reason for authentication failure is that the
+authentication key stored in the node configuration file, does not
+match the key on record. 
+
+There are two possible steps to resolve the problem.
+
+1. If you have used an All-in-one BootCD that includes the plnode.txt file,
+    then please check your machine for any old boot media, either in the
+    floppy drive, or on a USB stick.  It is likely that an old configuration
+    is being used instead of the new configuration stored on the BootCD.
+Or, 
+2. If you are using Generic BootCD image, then regenerate the node 
+    configuration file by visiting:
+
+    https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+    Under 'Download', follow the 'Download plnode.txt file for %(hostname)s'
+    option, and save the downloaded file as plnode.txt on either a floppy 
+    disk or a USB flash drive.  Be sure the 'Boot State' is set to 'Boot', 
+    and, then reboot the node.
+
+If you have already performed this step and are still receiving this
+message, please reply so that we can help investigate the problem.
+"""
+     },
+
+    {'message_id': "notinstalled",
+     'subject': "%(hostname)s is not installed",
+     'template':
+"""
+%(hostname)s failed to boot because it has either never been
+installed, or the installation is corrupt.
+
+Please check if the hard drive has failed, and replace it if so. After
+doing so, visit:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+Change the 'Boot State' to 'Reinstall', and then reboot the node.
+
+If you have already performed this step and are still receiving this
+message, please reply so that we may investigate the problem.
+"""
+     },
+
+    {'message_id': "missingkernel",
+     'subject': "%(hostname)s is missing its production kernel",
+     'template':
+"""
+%(hostname)s failed to boot because the filesystem is missing its production
+kernel.
+
+No action is needed from you at this time; this message is merely
+informational.
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+We will investigate the problem shortly.
+"""
+     },
+
+    {'message_id': "filesystemcorrupted",
+     'subject': "%(hostname)s may have corrupt filesystem",
+     'template':
+"""
+%(hostname)s failed to boot because the filesystem appears to be corrupted. 
+
+No action is needed from you at this time; this message is merely
+informational.
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+We will investigate the problem shortly.
+"""
+     },
+
+    {'message_id': "mountfailed",
+     'subject': "%(hostname)s could not mount filesystem",
+     'template':
+"""
+%(hostname)s failed to boot because the boot scripts could not mount the 
+filesystem.
+
+This could be for a number of reasons.  No action is needed from you at this
+time; this message is merely informational.  
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+We will investigate the problem shortly.
+"""
+     },
+
+    {'message_id': "hostnamenotresolve",
+     'subject': "%(hostname)s does not resolve",
+     'template':
+"""
+%(hostname)s failed to boot because its hostname does not resolve, or
+does resolve but does not match its configured IP address.
+
+Please check the network settings for the node, especially its
+hostname, IP address, and DNS servers, by visiting:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+Correct any errors, and change the 'Boot State' to 'Reinstall', and then
+reboot the node.
+
+If you have already performed this step and are still receiving this
+message, please reply so that we may investigate the problem.
+"""
+     },
+
+    # XXX N.B. I don't think these are necessary, since there's no
+    # way that the Boot Manager would even be able to contact the
+    # API to send these messages.
+
+    {'message_id': "noconfig",
+     'subject': "%(hostname)s does not have a configuration file",
+     'template': """
+%(hostname)s failed to boot because it could not find a PlanetLab
+configuration file. To create this file, visit:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+Click the Configuration File link, and save the downloaded file as
+plnode.txt on either a floppy disk or a USB flash drive.  Change the 
+'Boot State' to 'Reinstall', and then reboot the node.
+
+If you have already performed this step and are still receiving this
+message, please reply so that we may investigate the problem.
+"""
+     },
+
+    {'message_id': "nodetectednetwork",
+     'subject': "%(hostname)s has unsupported network hardware",
+     'template':
+"""
+
+%(hostname)s failed to boot because it has network hardware that is
+unsupported by the current production kernel. If it has booted
+successfully in the past, please try re-installing it by visiting:
+
+https://%(PLC_WWW_HOST)s:%(PLC_WWW_SSL_PORT)d/db/nodes/?id=%(node_id)d
+
+Change the 'Boot State' to 'Reinstall', and then reboot the node.
+
+If you have already performed this step and are still receiving this
+message, please reply so that we may investigate the problem.
+"""
+     },
+]
+
+for message in message_templates:
+    SetMessage(message)
diff --git a/plc.d/api b/plc.d/api
new file mode 100755 (executable)
index 0000000..4061b8d
--- /dev/null
+++ b/plc.d/api
@@ -0,0 +1,58 @@
+#!/bin/bash
+# $Id$
+# $URL$
+#
+# priority: 800
+#
+# Configure the API. Must be done after SSL certificates are generated
+# and before the API web server is brought up.
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+
+# Source function library and configuration
+. /etc/plc.d/functions
+. /etc/planetlab/plc_config
+local_config=/etc/planetlab/configs/site.xml
+
+# Be verbose
+set -x
+
+case "$1" in
+    start)
+       if [ "$PLC_API_ENABLED" != "1" ] ; then
+           exit 0
+       fi
+
+       MESSAGE=$"Configuring the API"
+       dialog "$MESSAGE"
+
+       # Make sure that the API maintenance account is protected by a
+       # password.
+       if [ -z "$PLC_API_MAINTENANCE_PASSWORD" ] ; then
+           PLC_API_MAINTENANCE_PASSWORD=$(uuidgen)
+           plc-config --category=plc_api --variable=maintenance_password --value="$PLC_API_MAINTENANCE_PASSWORD" --save=$local_config $local_config
+           service plc reload
+       fi
+
+       # Make sure that all PLC servers are allowed to access the API
+       # through the maintenance account.
+       PLC_API_MAINTENANCE_SOURCES=($((
+           for ip in $PLC_API_MAINTENANCE_SOURCES ; do
+               echo $ip
+           done
+           for server in API BOOT WWW ; do
+               hostname=PLC_${server}_HOST
+               gethostbyname ${!hostname}
+           done
+        ) | sort -u))
+       PLC_API_MAINTENANCE_SOURCES=${PLC_API_MAINTENANCE_SOURCES[*]}
+       plc-config --category=plc_api --variable=maintenance_sources --value="$PLC_API_MAINTENANCE_SOURCES" --save=$local_config $local_config
+       service plc reload
+
+       result "$MESSAGE"
+       ;;
+esac
+
+exit $ERRORS
diff --git a/plc.d/db b/plc.d/db
new file mode 100755 (executable)
index 0000000..a295aad
--- /dev/null
+++ b/plc.d/db
@@ -0,0 +1,193 @@
+#!/bin/bash
+# $Id$
+# $URL$
+#
+# priority: 900
+#
+# Bootstrap the database
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+
+# Source function library and configuration
+. /etc/plc.d/functions
+. /etc/planetlab/plc_config
+
+# Be verbose
+set -x
+
+# Export so that we do not have to specify -p to psql invocations
+export PGPORT=$PLC_DB_PORT
+
+# Updates the database by applying all migration scripts in
+# /usr/share/plc_api/migrations/N-up-*, where N is greater than the
+# current subversion. At least one of the migration scripts with the
+# same N must update plc_db_version.subversion.
+function migrate_db()
+{
+    subversion=$(psql -U $PLC_DB_USER --quiet --tuples-only --no-align -c \
+                "SELECT subversion FROM plc_db_version LIMIT 1" \
+                $PLC_DB_NAME 2>/dev/null || echo 0)
+    shopt -s nullglob
+    for file in /usr/share/plc_api/migrations/[0-9]*-up-* ; do
+       script=$(basename $file)
+       index=${script%-up*}
+       extension=${script##*.}
+       if [ $index -gt $subversion ] ; then
+           if [ "$extension" = "sql" ] ; then
+               dialog " - $script (dbdumped)"
+               dump_planetlab_db "before-$script"
+               psql -U $PLC_DB_USER -f $file $PLC_DB_NAME
+           elif [ -x $file ] ; then
+               dialog " - $script (dbdumped)"
+               dump_planetlab_db "before-$script"
+               $file
+           else
+               dialog "\nWarning: migration $file not executable"
+           fi
+           check
+       fi
+    done
+}
+
+function checkpoint_planetlab_db()
+{
+    dumpfile=$1
+    pg_dump -U $PLC_DB_USER $PLC_DB_NAME > $dumpfile
+    check
+}
+
+function restore_planetlab_db()
+{
+    dumpfile=$1
+    if [ -n "$dumpfile" ] ; then 
+       [ -f "$dumpfile" ] && psql -a -U $PLC_DB_USER $PLC_DB_NAME < $dumpfile
+       check
+    fi
+}
+
+# use a single date of this script invocation for the dump_*_db functions.
+DATE=$(date +"%Y-%m-%d-%H-%M-%S")
+
+# Dumps the database - optional argument to specify filename suffix
+function dump_planetlab_db()
+{
+    if [ -n "$1" ] ; then suffix="-$1" ; else suffix="" ; fi
+    dumpfile=/var/lib/pgsql/backups/$(date +"${PLC_DB_NAME}.${DATE}${suffix}.sql")
+    checkpoint_planetlab_db $dumpfile
+}
+
+function restore_drupal_db()
+{
+    dumpfile=$1
+    if [ -n "$dumpfile" ] ; then 
+       [ -f "$dumpfile" ] && psql -a -U $PLC_DB_USER drupal < $1
+       check
+    fi
+}
+
+function checkpoint_drupal_db()
+{
+    dumpfile=$1
+    pg_dump -U $PLC_DB_USER drupal > $dumpfile
+    check
+}
+
+function dump_drupal_db()
+{
+    dumpfile=/var/lib/pgsql/backups/$(date +"drupal.${DATE}.sql")
+    checkpoint_drupal_db $dumpfile
+    check
+}
+
+# Clean up old backups
+function clean_dumps()
+{
+    find /var/lib/pgsql/backups '(' -name "$PLC_DB_NAME.*.sql" -o -name "drupal.*.sql" ')' -a -atime +15 | xargs rm -f
+    check
+}
+
+[ $PLC_DB_ENABLED -ne 1 ] && exit 0
+case "$1" in
+    start)
+       MESSAGE=$"Bootstrapping the database"
+       dialog "$MESSAGE"
+
+       # Apply schema updates
+       migrate_db
+
+       # Update the maintenance account username. This can't be
+       # done through the api-config script since it uses the
+       # maintenance account to access the API. The maintenance
+       # account should be person_id 1 since it is created by the
+       # DB schema itself.
+       psql -U $PLC_DB_USER -c "UPDATE persons SET email='$PLC_API_MAINTENANCE_USER' WHERE person_id=1" $PLC_DB_NAME
+
+       # Update the Drupal site_name variable
+       # also turn off drupal native user registration
+       psql -U $PLC_DB_USER drupal <<EOF
+DELETE FROM variable WHERE name = 'site_name';
+INSERT INTO variable (name, value) VALUES ('site_name', 's:${#PLC_NAME}:"$PLC_NAME";');
+DELETE FROM variable WHERE name = 'user_register';
+INSERT INTO variable (name, value) VALUES ('user_register', 's:1:"0";');
+DELETE FROM cache;
+EOF
+
+       # Bootstrap the DB
+       db-config
+       check
+
+       result "$MESSAGE"
+       ;;
+
+    migrate)
+       MESSAGE=$"Migrating the database"
+       dialog "$MESSAGE"
+
+       migrate_db
+       result "$MESSAGE"
+       ;;
+
+    dump)
+       MESSAGE=$"Dumping the databases in /var/lib/pgsql/backups"
+       dialog "$MESSAGE"
+
+       dump_planetlab_db
+       dump_drupal_db
+       result "$MESSAGE"
+       ;;
+
+    checkpoint)
+       MESSAGE=$"Checkpointing the databases"
+       checkpoint_planetlab_db $2
+       checkpoint_drupal_db $3
+       ;;
+
+    restore)
+       MESSAGE=$"Restoring the databases from checkpoint files"
+       restore_planetlab_db $2
+       restore_drupal_db $3
+       ;;
+
+    clean-dump)
+       MESSAGE=$"Cleaning old database dumps"
+       dialog "$MESSAGE"
+
+       clean_dumps
+       result "$MESSAGE"
+       ;;
+
+    stop)
+       MESSAGE="Ignoring request to stop myplc databases"
+       dialog "$MESSAGE"
+       result ""
+       ;;
+
+    *)
+        echo "Usage: $0 [start|migrate|dump|checkpoint|restore|clean-dump|stop]"
+       exit 1
+       ;;
+esac
+
+exit $ERRORS
diff --git a/plc.d/postgresql b/plc.d/postgresql
new file mode 100755 (executable)
index 0000000..defab86
--- /dev/null
@@ -0,0 +1,196 @@
+#!/bin/bash
+# $Id$
+# $URL$
+#
+# priority: 700
+#
+# Manage the PostgreSQL database server
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+
+# Source function library and configuration
+. /etc/plc.d/functions
+. /etc/planetlab/plc_config
+local_config=/etc/planetlab/configs/site.xml
+
+# Be verbose
+set -x
+
+# Default locations
+PGDATA=/var/lib/pgsql/data
+postgresql_conf=$PGDATA/postgresql.conf
+pghba_conf=$PGDATA/pg_hba.conf
+
+# Export so that we do not have to specify -p to psql invocations
+export PGPORT=$PLC_DB_PORT
+
+# /etc/init.d/postgresql always returns 0, even on failure
+postgresql_start ()
+{
+    # start() always returns 0
+    (exec 3>&- 4>&- ; service postgresql start)
+
+    # status() will still return 0 even while still initializing
+    if status postmaster && [ -f /var/lock/subsys/postgresql ] ; then
+       # The only way we can be sure is if we can access it
+       for i in $(seq 1 10) ; do
+           # Must do this as the postgres user initially (before we
+           # fix pg_hba.conf to passwordless localhost access).
+           su -c 'psql -U postgres -c "" template1' postgres && return 0
+           sleep 1
+       done
+    fi
+
+    return 1
+}
+
+postgresql_init ()
+{
+    service postgresql initdb &> /dev/null || :
+    postgresql_start
+}
+
+case "$1" in
+    start)
+       if [ "$PLC_DB_ENABLED" != "1" ] ; then
+           exit 0
+       fi
+
+       MESSAGE=$"Starting PostgreSQL server"
+       dialog "$MESSAGE"
+
+       # Set data directory and redirect startup output to /var/log/pgsql
+       mkdir -p /etc/sysconfig/pgsql
+       (
+           echo "PGDATA=$PGDATA"
+           echo "PGLOG=/var/log/pgsql"
+           echo "PGPORT=$PLC_DB_PORT"
+       ) >>/etc/sysconfig/pgsql/postgresql
+
+       # Fix ownership (rpm installation may have changed it)
+       chown -R -H postgres:postgres $(dirname $PGDATA)
+
+       # PostgreSQL must be started at least once to bootstrap
+       # /var/lib/pgsql/data
+       if [ ! -f $postgresql_conf ] ; then
+           postgresql_init
+           check
+           service postgresql stop
+           check
+       fi
+
+       # Enable DB server. PostgreSQL >=8.0 defines listen_addresses,
+       # PostgreSQL 7.x uses tcpip_socket.
+       if grep -q listen_addresses $postgresql_conf ; then
+           sed -i -e '/^listen_addresses/d' $postgresql_conf
+           echo "listen_addresses = '*'" >>$postgresql_conf
+       elif grep -q tcpip_socket $postgresql_conf ; then
+           sed -i -e '/^tcpip_socket/d' $postgresql_conf
+           echo "tcpip_socket = true" >>$postgresql_conf
+       fi
+
+       # Disable access to all DBs from all hosts
+       sed -i -e '/^\(host\|local\)/d' $pghba_conf
+
+       # Enable passwordless localhost access
+       echo "local all all trust" >>$pghba_conf
+
+       # Enable access from the API, boot, and web servers
+       PLC_API_IP=$(gethostbyname $PLC_API_HOST)
+       PLC_BOOT_IP=$(gethostbyname $PLC_BOOT_HOST)
+       PLC_WWW_IP=$(gethostbyname $PLC_WWW_HOST)
+       ip_failure=0
+       if [ -z "$PLC_API_IP" ] ; then
+           MESSAGE=$"PLC_API_IP is not set"
+           dialog "$MESSAGE"
+           ip_failure=1
+       fi
+       if [ -z "$PLC_BOOT_IP" ] ; then
+           MESSAGE=$"PLC_BOOT_IP is not set"
+           dialog "$MESSAGE"
+           ip_failure=1
+       fi
+       if [ -z "$PLC_WWW_IP" ] ; then
+           MESSAGE=$"PLC_WWW_IP is not set"
+           dialog "$MESSAGE"
+           ip_failure=1
+       fi
+       if [ $ip_failure -eq 1 ] ; then
+           /bin/false
+           check
+       fi
+
+       (
+           echo "host $PLC_DB_NAME $PLC_DB_USER 127.0.0.1/32 password"
+           echo "host $PLC_DB_NAME $PLC_DB_USER $PLC_API_IP/32 password"
+           echo "host $PLC_DB_NAME $PLC_DB_USER $PLC_BOOT_IP/32 password"
+           echo "host $PLC_DB_NAME $PLC_DB_USER $PLC_WWW_IP/32 password"
+           # Drupal also uses PostgreSQL
+           echo "host drupal $PLC_DB_USER 127.0.0.1/32 password"
+           echo "host drupal $PLC_DB_USER $PLC_WWW_IP/32 password"
+       ) >>$pghba_conf
+
+       # Append site-specific access rules
+       for file in $pghba_conf.d/*.conf ; do
+           cat "$file" >>$pghba_conf
+       done
+
+       # Fix ownership (sed -i changes it)
+       chown postgres:postgres $postgresql_conf $pghba_conf
+
+       # Start up the server
+       postgresql_start
+       check
+
+       # Create/update the unprivileged database user and password
+       if [ -z "$PLC_DB_PASSWORD" ] ; then
+           PLC_DB_PASSWORD=$(uuidgen)
+           plc-config --category=plc_db --variable=password --value="$PLC_DB_PASSWORD" --save=$local_config $local_config
+           service plc reload
+       fi
+       if ! psql -U $PLC_DB_USER -c "" template1 >/dev/null 2>&1 ; then
+           psql -U postgres -c "CREATE USER $PLC_DB_USER PASSWORD '$PLC_DB_PASSWORD'" template1
+       else
+           psql -U postgres -c "ALTER USER $PLC_DB_USER WITH PASSWORD '$PLC_DB_PASSWORD'" template1
+       fi
+       check
+
+       # Create the databases if necessary
+       if ! psql -U $PLC_DB_USER -c "" $PLC_DB_NAME >/dev/null 2>&1 ; then
+           createdb -U postgres --template=template0 --encoding=UNICODE --owner=$PLC_DB_USER $PLC_DB_NAME
+           psql -U $PLC_DB_USER -f /usr/share/plc_api/$PLC_DB_NAME.sql $PLC_DB_NAME
+       fi
+       check
+       if ! psql -U $PLC_DB_USER -c "" drupal >/dev/null 2>&1 ; then
+           createdb -U postgres --template=template0 --encoding=UNICODE --owner=$PLC_DB_USER drupal
+            psql -U $PLC_DB_USER -f /var/www/html/database/database.pgsql drupal 
+       fi
+       check
+
+       result "$MESSAGE"
+       ;;
+
+    stop)
+       MESSAGE=$"Stopping PostgreSQL server"
+       dialog "$MESSAGE"
+
+       # Drop the current user in case the username changes
+       psql -U postgres -c "DROP USER $PLC_DB_USER" template1
+
+       # WARNING: If the DB name changes, the old DB will be left
+       # intact and a new one will be created. If it changes
+       # back, the old DB will not be re-created.
+
+       # Shut down the server
+       service postgresql stop
+
+       # /etc/init.d/postgresql fails if it is not running
+       [ "$PLC_DB_ENABLED" = 1 ] && check
+
+       result "$MESSAGE"
+       ;;
+esac
+
+exit $ERRORS