From 5afb8089a48433612fb10bed3c609006882b9859 Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Tue, 19 Jan 2010 11:55:09 +0000 Subject: [PATCH] split plc.d/ and db-config.d between myplc and plcapi modules as a first step conf_files would move to nodeconfig some other stuff ready to move to the appropriat module as well --- PLCAPI.spec | 17 +- db-config.d/000-functions | 144 ++++++++++++++++ db-config.d/001-admin_user | 24 +++ db-config.d/002-system_site | 69 ++++++++ db-config.d/010-slice_tags | 167 +++++++++++++++++++ db-config.d/011-node_tags | 21 +++ db-config.d/012-interface_tags | 26 +++ db-config.d/013-wireless_tags | 48 ++++++ db-config.d/020-boot_states | 23 +++ db-config.d/050-pcu_types | 61 +++++++ db-config.d/060-messages | 293 +++++++++++++++++++++++++++++++++ plc.d/api | 58 +++++++ plc.d/db | 193 ++++++++++++++++++++++ plc.d/postgresql | 196 ++++++++++++++++++++++ 14 files changed, 1339 insertions(+), 1 deletion(-) create mode 100644 db-config.d/000-functions create mode 100644 db-config.d/001-admin_user create mode 100644 db-config.d/002-system_site create mode 100644 db-config.d/010-slice_tags create mode 100644 db-config.d/011-node_tags create mode 100644 db-config.d/012-interface_tags create mode 100644 db-config.d/013-wireless_tags create mode 100644 db-config.d/020-boot_states create mode 100644 db-config.d/050-pcu_types create mode 100644 db-config.d/060-messages create mode 100755 plc.d/api create mode 100755 plc.d/db create mode 100755 plc.d/postgresql diff --git a/PLCAPI.spec b/PLCAPI.spec index 0b2cdd9..39066fa 100644 --- a/PLCAPI.spec +++ b/PLCAPI.spec @@ -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 < - 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 - 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 index 0000000..70fa078 --- /dev/null +++ b/db-config.d/000-functions @@ -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 index 0000000..8797164 --- /dev/null +++ b/db-config.d/001-admin_user @@ -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 index 0000000..ced75a7 --- /dev/null +++ b/db-config.d/002-system_site @@ -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 index 0000000..282bc0c --- /dev/null +++ b/db-config.d/010-slice_tags @@ -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 index 0000000..3d03521 --- /dev/null +++ b/db-config.d/011-node_tags @@ -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 index 0000000..e184c39 --- /dev/null +++ b/db-config.d/012-interface_tags @@ -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 index 0000000..77c6683 --- /dev/null +++ b/db-config.d/013-wireless_tags @@ -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 index 0000000..b7a8a5b --- /dev/null +++ b/db-config.d/020-boot_states @@ -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 index 0000000..ef53713 --- /dev/null +++ b/db-config.d/050-pcu_types @@ -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 index 0000000..78eaa71 --- /dev/null +++ b/db-config.d/060-messages @@ -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 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 +# 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 index 0000000..a295aad --- /dev/null +++ b/plc.d/db @@ -0,0 +1,193 @@ +#!/bin/bash +# $Id$ +# $URL$ +# +# priority: 900 +# +# Bootstrap the database +# +# Mark Huang +# 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 < +# 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 -- 2.43.0