Skip to content
Browse files

rest of main iaas from 2009-01-10

  • Loading branch information...
1 parent ec0e8d1 commit 46e8b0f5709e5a49fecf77691a0eb63f018186e1 @timf timf committed Sep 29, 2009
Showing with 20,703 additions and 0 deletions.
  1. +636 −0 backend/aux/foreign-subnet.py
  2. +37 −0 backend/blankcreate.sh
  3. +664 −0 backend/dhcp-conf-alter.py
  4. +372 −0 backend/dhcp-config.sh
  5. +64 −0 backend/dhcpd.conf.example
  6. +248 −0 backend/ebtables-config.sh
  7. +1,394 −0 backend/install.py
  8. +333 −0 backend/mount-alter.sh
  9. +41 −0 backend/swapcreate.sh
  10. +331 −0 backend/worksp.conf.example
  11. +563 −0 backend/worksp.py
  12. +9 −0 backend/workspace/__init__.py
  13. +43 −0 backend/workspace/err.py
  14. +1,267 −0 backend/workspace/util/IPy.py
  15. +50 −0 backend/workspace/util/__init__.py
  16. +39 −0 backend/workspace/util/config.py
  17. +74 −0 backend/workspace/util/control.py
  18. 0 backend/workspace/vms/__init__.py
  19. +28 −0 backend/workspace/vms/images.py
  20. 0 backend/workspace/vms/xen/__init__.py
  21. +314 −0 backend/workspace/vms/xen/xen_networking.py
  22. +4,037 −0 backend/workspace/vms/xen/xen_v2.py
  23. +50 −0 backend/workspace/vms/xen/xen_v3.py
  24. +11 −0 bin/all-build-and-install.sh
  25. +11 −0 bin/all-build.sh
  26. +11 −0 bin/all-clean.sh
  27. +11 −0 bin/all-install.sh
  28. +11 −0 bin/all-uninstall.sh
  29. +11 −0 bin/clients-only-build-and-install.sh
  30. +11 −0 bin/clients-only-build.sh
  31. +11 −0 bin/clients-only-install.sh
  32. +12 −0 bin/delete-persistence-directory.sh
  33. +34 −0 bin/lib/gt4.0/build/build.properties
  34. +216 −0 bin/lib/gt4.0/build/build.xml
  35. +38 −0 bin/lib/gt4.0/build/run.sh
  36. +41 −0 bin/lib/gt4.0/dist/build.properties
  37. +216 −0 bin/lib/gt4.0/dist/build.xml
  38. +20 −0 bin/lib/gt4.0/dist/scripts/deploy-client-gars.sh
  39. +193 −0 bin/lib/gt4.0/dist/topdocs/LICENSE.txt
  40. +21 −0 bin/lib/gt4.0/dist/topdocs/README.txt
  41. +5 −0 bin/lib/notes.txt
  42. +17 −0 metadata/java/source/build.properties
  43. +131 −0 metadata/java/source/build.xml
  44. +680 −0 metadata/java/source/src/org/nimbustools/metadataserver/defaults/DefaultMetadataServer.java
  45. +98 −0 metadata/java/source/src/org/nimbustools/metadataserver/defaults/HTTPListener.java
  46. +72 −0 metadata/java/source/src/org/nimbustools/metadataserver/defaults/HTTPLogging.java
  47. +127 −0 metadata/java/source/src/org/nimbustools/metadataserver/defaults/MetadataRequestHandler.java
  48. +21 −0 metadata/java/source/src/org/nimbustools/metadataserver/defaults/default-ehcache.xml
  49. +3 −0 pilot/genapidoc.sh
  50. +57 −0 pilot/generate-index.py
  51. +4,692 −0 pilot/workspacepilot.py
  52. +105 −0 plugins/authz/python/build.xml
  53. +8 −0 plugins/authz/python/etc/example.py
  54. +62 −0 plugins/authz/python/etc/printinfo.py
  55. BIN plugins/authz/python/lib-undeployed/globus_voms_interceptors.jar
  56. BIN plugins/authz/python/lib-undeployed/gridshib-gt-0_3_3.jar
  57. +9 −0 plugins/authz/python/lib-undeployed/notes.txt
  58. +145 −0 plugins/authz/python/lib/jython.LICENSE
  59. BIN plugins/authz/python/lib/jython.jar
  60. +11 −0 plugins/authz/python/lib/notes.txt
  61. +72 −0 plugins/authz/python/src/org/globus/jython/Jython.java
  62. +25 −0 plugins/authz/python/src/org/globus/jython/JythonLoader.java
  63. +40 −0 plugins/authz/python/src/org/globus/jython/log.java
  64. +132 −0 plugins/authz/python/src/org/globus/workspace/interceptors/jython/PythonAuthorization.java
  65. +83 −0 plugins/authz/python/src/org/globus/workspace/interceptors/jython/Shib.java
  66. +31 −0 plugins/authz/python/src/org/globus/workspace/interceptors/jython/ShibUtil.java
  67. +85 −0 plugins/authz/python/src/org/globus/workspace/interceptors/jython/Voms.java
  68. +31 −0 plugins/authz/python/src/org/globus/workspace/interceptors/jython/VomsUtil.java
  69. +429 −0 plugins/authz/python/src/org/globus/workspace/interceptors/jython/WorkspacePythonAuthorization.java
  70. +77 −0 plugins/authz/voms/bootstrap/org/globus/MajorMinorVersion.java
  71. +288 −0 plugins/authz/voms/build.xml
  72. +1 −0 plugins/authz/voms/etc/sample-attr-authz
  73. BIN plugins/authz/voms/lib/glite-security-util-java.jar
  74. +122 −0 plugins/authz/voms/src-proxies/4.0/org/globus/voms/PDP.java
  75. +78 −0 plugins/authz/voms/src-proxies/4.0/org/globus/voms/PIP.java
  76. +156 −0 plugins/authz/voms/src-proxies/4.1+/org/globus/voms/PDP.java
  77. +103 −0 plugins/authz/voms/src-proxies/4.1+/org/globus/voms/PIP.java
  78. +154 −0 plugins/authz/voms/src/org/globus/voms/impl/ACLPDP.java
  79. +31 −0 plugins/authz/voms/src/org/globus/voms/impl/PDPDecision.java
  80. +40 −0 plugins/authz/voms/src/org/globus/voms/impl/VomsConstants.java
  81. +55 −0 plugins/authz/voms/src/org/globus/voms/impl/VomsCredentialInformation.java
  82. +177 −0 plugins/authz/voms/src/org/globus/voms/impl/VomsCredentialPIP.java
  83. +635 −0 plugins/authz/voms/src/org/globus/voms/impl/VomsPDP.java
  84. +122 −0 plugins/authz/voms/src/org/globus/voms/impl/VomsPDPPolicy.java
  85. +20 −0 plugins/authz/voms/src/org/globus/wsrf/security/authorization/attributes/AttributeInformation.java
View
636 backend/aux/foreign-subnet.py
@@ -0,0 +1,636 @@
+#!/usr/bin/env python
+
+# ================================ LICENSE ====================================
+lic="""
+Copyright 1999-2008 University of Chicago
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License. You may obtain a copy
+of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations
+under the License.
+
+For more information see: http://workspace.globus.org
+"""
+
+
+# ================================ TASKDEF ====================================
+
+
+# ignore this part, edit below
+TASKS=[]
+class Task:
+ def __init__(self, ip=None, network=None, broadcast=None,
+ veth=None, vif=None, bridge=None, original=None,
+ physnic=None, formac=None):
+ self.ipaddress = ip # 1.2.3.4
+ self.network = network # "1.2.3.0/24"
+ self.broadcast = broadcast # "1.2.3.255"
+ self.vethname = veth # "veth1"
+ self.vifname = vif # "vif0.1"
+ self.bridgename = bridge # "xenbr0"
+ self.orignic = original # "eth0"
+ self.physicalnic = physnic # "peth0"
+ self.macdigits = formac
+ self.slashpart = None # ignore
+
+
+# SEE EXPLANATION BELOW BEFORE CHANGING THESE:
+
+TASKS.append(Task( ip="125.125.125.125",
+ network="125.125.125.0/24",
+ broadcast="125.125.125.255",
+ veth="veth1",
+ vif="vif0.1",
+ bridge="xenbr0",
+ original="eth0",
+ physnic="peth0",
+ formac="CF" ))
+
+TASKS.append(Task( ip="172.20.6.125",
+ network="172.20.6.0/24",
+ broadcast="172.20.6.255",
+ veth="veth2",
+ vif="vif0.2",
+ bridge="xenbr0",
+ original="eth0",
+ physnic="peth0",
+ formac="DF" ))
+
+# ============================== EXPLANATION ==================================
+doc="""
+
+This script will allow you to host VMs on a foreign subnet without needing
+the VMM to have a real presence on that subnet. A presence is required
+currently for DHCP delivery of an address using *non modified* DHCP
+implementations.
+
+
+ ======= UNIQUE FAKE IP PER VMM =======
+
+In the case where you have subnets that can accomodate one extra address *per
+VMM* you can configure an alias for that extra IP and be done. For example,
+in domain zero run:
+
+ifconfig eth0:0 10.20.0.5 netmask 255.255.0.0
+
+And configure the DHCP conf file with:
+
+ shared-network networks_ether1 {
+
+ # original subnet
+ subnet W.X.Y.Z netmask 255.255.255.0 {
+ }
+
+ # new, fake subnet, this VMM has 10.20.0.5 on it, etc.
+ subnet 10.20.0.0 netmask 255.255.0.0 {
+ }
+ }
+
+And you're good to go.
+
+
+ ======= ONE FAKE IP FOR EVERY VMM (this script) =======
+
+But that requires each VMM has a different extra IP on the subnet (otherwise
+there can be IP and ARP confusion).
+
+This script is mainly motivated by the public IP case which is normally a
+scarce resource.
+
+*** THIS IS AN ADVANCED CONFIGURATION ***
+
+Instead of requiring an extra public IP per VMM just to host VMs with public
+IPs, this script allows you to configure the SAME ALIAS IP ON EVERY VMM.
+
+The ebtables rules this script sets up block off ALL traffic to/from the
+cluster for this address, so it's as if it is really not in use. It's only in
+use on the local network stack on each VMM.
+
+It must not be used for any hostname value, any configuration value, etc. It
+must be considered an "unused" IP for normal cluster operations and not ever
+be addressed or needed for anything.
+
+Above, in the "TASKDEF" section, for each "foreign subnet" to host you must
+configure:
+
+a) the special IP
+b) the network with slash abbreviation (for example "1.2.3.0/24")
+c) the broadcast (for example "1.2.3.255")
+d) the virtual interface name (for example "veth1")
+e) the corresponding Xen vif (for example "vif0.1")
+f) the bridge to add the vif to (for example "xenbr0")
+g) the original dom0 interface (for example "eth0")
+h) the actual physical NIC interface (for example "peth0")
+i) two hex digits to use for fake MAC (for example "FF")
+
+If you have multiple physical NICs, take care to start at the appropriate
+"veth" interface ("eth1" and the corresponding "vif0.1" may already be in use).
+
+Note that the value of "e", the bridge, must be the same bridge that workspace
+NICs for the intended network are bridged to. If you have just one physical
+NIC, there is likely only one to choose for (for example "xenbr0").
+
+Regarding "g", the script will create a fake MAC address for the fake NIC,
+based on the original dom0 interface (for example "eth0"). It will replace
+the third and fourth hex digits with something else you provide. The strategy
+here is to create MAC addresses that are unique yet different on each node,
+as a contingency because the bridge might be confused by identical MACs. So
+it is based off the node's unique MAC and assumes the cluster's entire real MAC
+set has different 3rd and 4th digits. Also assumes workspace MAC prefix will
+never be able to conflict either (this prefix is usually defined in the
+service's network settings).
+
+
+ROUTING -- ** WARNING **
+
+If you set up a "foreign subnet" for DHCP by normal means (for example using
+"ifconfig") you would typically cause a routing entry to be added for the
+associated subnet.
+
+This can not happen (since the kernel will now choose this fake address as the
+source IP for anything heading to that subnet -- and traffic from this fake
+address is blocked).
+
+If your cluster node relies on the network for logins (LDAP, SSH's UseDNS,
+etc.), in particular SOME SERVICE ADDRESS ON THE NEW SUBNET, there is a chance
+you could cut yourself off entirely from the node if you log out and that
+'veth' interface is part of the routing table.
+
+It is hard to make sure this is done right automatically, so all this script
+does is remove the net, for example "route del -net 125.125.125.0/24 veth1"
+
+If something needs to be added back in, SCRIPT THE NECESSARY ROUTING ADDITION
+after this script runs.
+
+"""
+
+
+
+# ================================ IMPORTS ====================================
+
+import fcntl
+import os
+import socket
+import struct
+import sys
+
+# ================================== MISC =====================================
+
+# from python mailing list:
+def _ifinfo(sock, addr, ifname):
+ iface = struct.pack('256s', ifname[:15])
+ info = fcntl.ioctl(sock.fileno(), addr, iface)
+ if addr == 0x8927:
+ hwaddr = []
+ for char in info[18:24]:
+ hwaddr.append(hex(ord(char))[2:])
+ return ':'.join(hwaddr)
+ else:
+ return socket.inet_ntoa(info[20:24])
+
+# from python mailing list:
+def ifconfig(ifname):
+ ifreq = {'ifname': ifname}
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ ifreq['addr'] = _ifinfo(sock, 0x8915, ifname) # SIOCGIFADDR
+ ifreq['brdaddr'] = _ifinfo(sock, 0x8919, ifname) # SIOCGIFBRDADDR
+ ifreq['netmask'] = _ifinfo(sock, 0x891b, ifname) # SIOCGIFNETMASK
+ ifreq['hwaddr'] = _ifinfo(sock, 0x8927, ifname) # SIOCSIFHWADDR
+ except:
+ pass
+ # exceptions are normal...
+ sock.close()
+ return ifreq
+
+
+# ================================ EBTABLES ===================================
+
+def ebtables_rule1(task):
+ """ebtables -A FORWARD -p IPv4 -o peth0 --ip-src 125.125.125.125 -j DROP
+
+ English: "discard any packet heading out on the physical NIC to the
+ cluster (bridged out on the peth0 interface) with an IP source address
+ of 125.125.125.125"
+ """
+ return "FORWARD -p IPv4 -o %s --ip-src %s -j DROP" % (task.physicalnic, task.ipaddress)
+
+def ebtables_rule2(task):
+ """ebtables -A FORWARD -p IPv4 -o peth0 --ip-dst 125.125.125.125 -j DROP
+
+ English: "discard any packet heading out on the physical NIC to the
+ cluster (bridged out on the peth0 interface) with an IP destination address
+ of 125.125.125.125"
+ """
+ return "FORWARD -p IPv4 -o %s --ip-dst %s -j DROP" % (task.physicalnic, task.ipaddress)
+
+def ebtables_rule3(task):
+ """ebtables -A FORWARD -p IPv4 -i peth0 --ip-src 125.125.125.125 -j DROP
+
+ English: "discard any packet coming from the physical NIC, from the
+ cluster (bridged in on the peth0 interface), with an IP source address
+ of 125.125.125.125"
+ """
+ return "FORWARD -p IPv4 -i %s --ip-src %s -j DROP" % (task.physicalnic, task.ipaddress)
+
+def ebtables_rule4(task):
+ """ebtables -A FORWARD -p IPv4 -i peth0 --ip-dst 125.125.125.125 -j DROP
+
+ English: "discard any packet coming from the physical NIC, from the
+ cluster (bridged in on the peth0 interface), with an IP destination address
+ of 125.125.125.125
+ """
+ return "FORWARD -p IPv4 -i %s --ip-dst %s -j DROP" % (task.physicalnic, task.ipaddress)
+
+def ebtables_rule5(task):
+ """ebtables -A FORWARD -p IPv4 --ip-proto udp --ip-dport 67 --ip-dst 125.125.125.125 -j ACCEPT
+
+ English: "accept any UDP packet addressed to 125.125.125.125 that is to
+ port 67". This lets the VMs contact this address for the DHCP protocol.
+ """
+ return "FORWARD -p IPv4 --ip-proto udp --ip-dport 67 --ip-dst %s -j ACCEPT" % (task.ipaddress)
+
+def ebtables_rule6(task):
+ """ebtables -A FORWARD -p IPv4 --ip-dst 125.125.125.125 -j DROP
+
+ English: "discard any IP packet addressed to 125.125.125.125". This
+ catches any other addressed packet from any VM on this VMM to the address.
+ The previous rule (#5) accepts OK packets (DHCP) and then this one throws
+ out anything else.
+ """
+ return "FORWARD -p IPv4 --ip-dst %s -j DROP" % (task.ipaddress)
+
+def ebtables_nat_rule1(task):
+ """ebtables -t nat -A PREROUTING -p ARP -i peth0 --arp-op Request --arp-ip-dst 125.125.125.125 -j DROP
+
+ English: "discard any ARP packet coming from the physical NIC, from the
+ cluster (bridged in on the peth0 interface), with a query address of
+ 125.125.125.125"
+ """
+ return "PREROUTING -p ARP -i %s --arp-op Request --arp-ip-dst %s -j DROP" % (task.physicalnic, task.ipaddress)
+
+def ebtables_run(rule, add, natrule=False):
+ """if add is True, implements -A (append), if false, -D (delete)"""
+
+ if add:
+ flag = "-A"
+ else:
+ flag = "-D"
+
+ if natrule:
+ cmd = "ebtables -t nat %s %s" % (flag, rule)
+ else:
+ cmd = "ebtables %s %s" % (flag, rule)
+
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ if add:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+ else:
+ print "Removal command failed: '%s'" % cmd
+
+def ebtables_adjust(task, add):
+ """if add is True, these implement -A (append), if false, -D (delete)"""
+
+ ebtables_run(ebtables_nat_rule1(task), add, natrule=True)
+
+ # The order of these rules is very important.
+ ebtables_run(ebtables_rule1(task), add)
+ ebtables_run(ebtables_rule2(task), add)
+ ebtables_run(ebtables_rule3(task), add)
+ ebtables_run(ebtables_rule4(task), add)
+ ebtables_run(ebtables_rule5(task), add)
+ ebtables_run(ebtables_rule6(task), add)
+
+# =============================== VIF SETUP ===================================
+
+def vif_setup(task):
+
+ cmd = "ifconfig %s -multicast" % task.vifname
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+ cmd = "ifconfig %s -arp" % task.vifname
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+ cmd = "ifconfig %s up" % task.vifname
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+# ============================= VIF + BRIDGE ==================================
+
+def vif_bridge_add(task):
+
+ cmd = "brctl addif %s %s" % (task.bridgename, task.vifname)
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+def vif_bridge_del(task):
+
+ cmd = "brctl delif %s %s" % (task.bridgename, task.vifname)
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+
+# ================================ RUN ONE ====================================
+
+def get_mac(task):
+
+ interface = task.orignic
+ try:
+ ifreq = ifconfig(interface)
+ except:
+ return None
+ #print ifreq
+ try:
+ #print " - Found '%s' address for %s" % (ifreq['addr'], interface)
+ print " - Found '%s' MAC for %s" % (ifreq['hwaddr'], interface)
+ return ifreq['hwaddr']
+ except KeyError:
+ return None
+
+def get_new_mac(task, currmac):
+ parts = currmac.split(":")
+ if len(parts) != 6:
+ msg = "unexpected, current MAC doesn't have six parts? '%s'" % currmac
+ raise Exception(msg)
+ parts[1] = task.macdigits
+ newparts = []
+ for part in parts:
+ if len(part) == 1:
+ newparts.append("0%s" % part.upper())
+ elif len(part) == 2:
+ newparts.append(part.upper())
+ else:
+ msg = "current MAC looks invalid '%s', part with len > 2" % currmac
+ raise Exception(msg)
+ return ":".join(newparts)
+
+def set_mac(task, newmac):
+
+ cmd = "ifconfig %s hw ether %s" % (task.vethname, newmac)
+ #cmd = "ip link set ${netdev} addr fe:ff:ff:ff:ff:ff" % ()
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+def set_ip(task):
+
+ cmd = "ip addr flush %s" % (task.vethname)
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+ cmd = "ip addr add %s%s broadcast %s dev %s" % (task.ipaddress, task.slashpart, task.broadcast, task.vethname)
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+ cmd = "ip link set dev %s up" % (task.vethname)
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+def remove_route(task):
+ cmd = "route del -net %s %s" % (task.network, task.vethname)
+ print " - Running: %s" % cmd
+ ret = os.system(cmd)
+ if ret != 0:
+ raise Exception("ERROR, command failed: '%s'" % cmd)
+
+def do_one_task(task):
+
+ ebtables_setup_started = False
+ vif_added_to_bridge = False
+
+ try:
+ # create MAC for veth (don't set it yet though)
+ currmac = get_mac(task)
+ if not currmac:
+ raise Exception("could not resolve interface '%s'" % task.orignic)
+
+ newmac = get_new_mac(task, currmac)
+
+ print " - Chose MAC '%s' for %s" % (newmac, task.vethname)
+
+ # set up ebtables rules for the address
+ ebtables_setup_started = True
+ ebtables_adjust(task, True)
+
+ # set up vif
+ vif_setup(task)
+
+ # add vif to bridge
+ vif_bridge_add(task)
+ vif_added_to_bridge = True
+
+ # set mac
+ set_mac(task, newmac)
+
+ # set IP
+ set_ip(task)
+
+ # routing
+ remove_route(task)
+
+ except:
+ if ebtables_setup_started:
+ print "*** BACKING OUT ebtables rules ***"
+ ebtables_adjust(task, False)
+ if vif_added_to_bridge:
+ print "*** BACKING OUT vif addition to bridge ***"
+ vif_bridge_del(task)
+ raise
+
+# =============================== VALIDATION ==================================
+
+def simple_ip_check(ip):
+
+ if not ip:
+ print "Problem: no IP defined"
+ return False
+
+ parts = ip.split(".")
+
+ if len(parts) != 4:
+ print "Problem: expecting four parts in IP: W.X.Y.Z"
+ return False
+
+ for part in parts:
+ try:
+ num = int(part)
+ except:
+ print "Problem: '%s' is not a number" % part
+ return False
+
+ for part in parts:
+ num = int(part)
+ if num < 0:
+ print "Problem: '%s' is negative? invalid IP" % part
+ return False
+ if num > 255:
+ print "Problem: '%s' is greater than 255? invalid IP" % part
+ return False
+
+ return True
+
+def check_str(something, name):
+
+ if not something:
+ print "No %s definition, invalid task" % name
+ return False
+
+ if not isinstance(something, str):
+ print "%s is not a string? invalid task" % name
+ return False
+
+ return True
+
+def check_task_dups(tasks):
+ ips = []
+ veths = []
+ vifs = []
+ macdigs = []
+
+ probstr = "Exiting, found a duplicate in different task definitions:"
+ for task in tasks:
+ if task.ipaddress in ips:
+ print "%s IP '%s'" % (probstr, task.ipaddress)
+ return False
+ else:
+ ips.append(task.ipaddress)
+
+ if task.vethname in veths:
+ print "%s veth '%s'" % (probstr, task.vethname)
+ return False
+ else:
+ veths.append(task.vethname)
+
+ if task.vifname in vifs:
+ print "%s vif '%s'" % (probstr, task.vifname)
+ return False
+ else:
+ vifs.append(task.vifname)
+
+ if task.macdigits in macdigs:
+ print "%s mac digits '%s'" % (probstr, task.macdigits)
+ return False
+ else:
+ macdigs.append(task.macdigits)
+
+ return True
+
+def check_task(task):
+ """allowed to alter task, e.g. mac prefix --> upper()"""
+
+ if not task:
+ print "problem with TASKS list setup ('no task')"
+ return False
+
+ if not check_str(task.vethname, "'veth'"):
+ return False
+ if not check_str(task.vifname, "'vif'"):
+ return False
+ if not check_str(task.bridgename, "'bridge'"):
+ return False
+ if not check_str(task.orignic, "'original nic'"):
+ return False
+ if not check_str(task.physicalnic, "'physical nic'"):
+ return False
+
+ if not check_str(task.ipaddress, "'IP'"):
+ return False
+ if not simple_ip_check(task.ipaddress):
+ return False
+
+ if not check_str(task.broadcast, "'broadcast'"):
+ return False
+ if not simple_ip_check(task.broadcast):
+ return False
+
+ if not check_str(task.network, "'mask /net'"):
+ return False
+
+ err = "expecting /net number like '/24'"
+ netparts = task.network.split("/")
+
+ if len(netparts) != 2:
+ print "%s, invalid task (expecting net/mask, e.g. 1.2.3.0/24)" % err
+ return False
+
+ if not simple_ip_check(netparts[0]):
+ return False
+
+ try:
+ somenum = int(netparts[1])
+ if somenum < 0 or somenum > 32:
+ print "%s, invalid task (num=%d)" % (err, somenum)
+ return False
+ task.slashpart = "/%d" % somenum
+ except:
+ print "%s, invalid task (not an integer)" % err
+ return False
+
+ name = "'mac digits' definition (formac)"
+ if not check_str(task.macdigits, name):
+ return False
+ if len(task.macdigits) != 2:
+ print "expecting 2 digits for %s, invalid task" % name
+ return False
+
+ task.macdigits = task.macdigits.upper()
+ valid = "0123456789ABCDEF"
+ for char in task.macdigits:
+ if char not in valid:
+ print "expecting hex digit for %s, invalid task" % name
+ print "offending letter is '%s'" % char
+ return False
+
+ return True
+
+# ================================== MAIN =====================================
+
+def main(argv=None):
+ if len(TASKS) == 0:
+ print "No task definition, exiting (see the explanation at the top of \
+this program"
+ return 1
+
+ for i,task in enumerate(TASKS):
+ print "** Checking task definition #%d" % (i+1)
+ if not check_task(task):
+ return 1
+
+ if not check_task_dups(TASKS):
+ return 1
+
+ for i,task in enumerate(TASKS):
+ print "** Running task #%d (%s: '%s')" % (i+1, task.vethname, task.ipaddress)
+ do_one_task(task)
+
+if __name__ == "__main__":
+ sys.exit(main())
+
View
37 backend/blankcreate.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+#############
+# ARGUMENTS #
+#############
+
+if [ $# -lt 2 ]; then
+ echo "ERROR: requires 2 arguments. Syntax: $0 <file> <megabytes>"
+ exit 1
+fi
+
+FILE=$1
+echo " blank partition: $FILE"
+MEGS=$2
+echo " size (megs): $MEGS"
+
+CMD="dd if=/dev/zero of=$FILE bs=1M seek=$MEGS count=1"
+echo " running: $CMD"
+
+$CMD
+if [ $? -ne 0 ]; then
+ echo "ERROR: Failed to make sparse image"
+ exit 1
+fi
+
+CMD="/sbin/mke2fs -F $FILE"
+echo " running: $CMD"
+
+$CMD
+if [ $? -ne 0 ]; then
+ echo "ERROR: Failed to make filesystem"
+ exit 1
+else
+ exit 0
+fi
+
+
View
664 backend/dhcp-conf-alter.py
@@ -0,0 +1,664 @@
+#!/usr/bin/env python
+
+# Copyright 1999-2006 University of Chicago
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy
+# of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# For more information see: http://workspace.globus.org
+
+import ConfigParser
+import fcntl
+import logging
+import optparse
+import os
+import re
+import stat
+import sys
+import time
+
+# lockfile's default path is conf file + suffix
+LOCKSUFFIX=".lock"
+
+log = logging.getLogger("dhcp-conf-alter")
+ch = logging.StreamHandler()
+ch.setLevel(logging.DEBUG)
+formatter = logging.Formatter("%(levelname)s: %(message)s")
+# for timestamp and line no
+#formatter = logging.Formatter("%(asctime)s (%(lineno)d) %(levelname)s - %(message)s")
+ch.setFormatter(formatter)
+log.addHandler(ch)
+
+# sledgehammer approach
+iprestr = r"^([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])\.([01]?\d\d?|2[0-4]\d|25[0-5])$"
+IPRE = re.compile(iprestr)
+macrestr = r"^([A-F]|[a-f]|[0-9])([A-F]|[a-f]|[0-9])\:([A-F]|[a-f]|[0-9])([A-F]|[a-f]|[0-9])\:([A-F]|[a-f]|[0-9])([A-F]|[a-f]|[0-9])\:([A-F]|[a-f]|[0-9])([A-F]|[a-f]|[0-9])\:([A-F]|[a-f]|[0-9])([A-F]|[a-f]|[0-9])\:([A-F]|[a-f]|[0-9])([A-F]|[a-f]|[0-9])$"
+MACRE = re.compile(macrestr)
+
+class InvalidInput(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+ def __str__(self):
+ return self.msg
+
+class DHCPConf:
+ def __init__(self, original_content):
+ self.original_content = original_content # list of lines
+ self.header = [] # list of lines to add before the hosts
+ self.all_hosts = {} # dict of DHCPConfEntry instances keyed by mac addr
+
+ def parse(self, verbose):
+ pass
+
+ def write(self, f, verbose):
+ pass
+
+ def add(self, entry):
+ if not self.original_content and not self.header:
+ raise Exception("parse() must be called first")
+ if not entry.mac:
+ raise Exception("entry has no MAC address")
+ if not entry.ip:
+ raise Exception("entry has IP address")
+ if not entry.hostname:
+ raise Exception("entry has no hostname")
+
+ # current impl assumes mac, ip, and hostname all unique
+ for ip in self.all_hosts:
+ if ip == entry.ip:
+ raise Exception("IP %s already present" % ip)
+ if self.all_hosts[ip].mac == entry.mac:
+ raise Exception("MAC %s already present" % entry.mac)
+ if self.all_hosts[ip].hostname == entry.hostname:
+ raise Exception("hostname %s already present" % entry.hostname)
+
+ self.all_hosts[entry.ip] = entry
+ log.info("added entry: %s" % entry.ip)
+
+ def remove(self, entry):
+ if not self.original_content and not self.header:
+ raise Exception("parse() must be called first")
+ if not entry.ip:
+ raise Exception("entry has no IP address")
+ if not self.all_hosts.has_key(entry.ip):
+ log.info("entry with IP address already not present")
+ return False
+ else:
+ del self.all_hosts[entry.ip]
+ log.info("deleted entry: %s" % entry.ip)
+ return True
+
+class DHCPConfEntry:
+ def __init__(self):
+ self.hostname = None
+ self.mac = None
+ self.ip = None
+ self.dns = [] # list
+ self.broadcast = None
+ self.gateway = None
+ self.subnetmask = None
+ self.default_lease = None
+ self.max_lease = None
+ self.comment = None
+
+# --------------------------- PARSING -------------------------- #
+
+def parse(opts):
+ """Returns instance of DHCPConf representing current configuration"""
+
+ f = None
+ try:
+ f = open(opts.policyfile, "r")
+ content = f.readlines()
+ finally:
+ if f:
+ f.close()
+
+ # todo: when more impls come along, make it easy to switch between them
+ conf = ISCDHCPConf(content)
+ conf.parse(opts.veryverbose)
+ return conf
+
+# ----------------------------- ADD ---------------------------- #
+
+def add(opts, conf):
+ """Adds an instance of DHCPConfEntry to a DHCPConf object or throws
+ exception"""
+
+ entry = DHCPConfEntry()
+ entry.ip = opts.ipaddress
+ entry.hostname = opts.hostname
+ entry.mac = opts.macaddress
+ entry.comment = "Added by dhcp-conf-alter @ %s" % time.ctime()
+ if opts.dns:
+ entry.dns = opts.dns # list
+ if opts.subnetmask:
+ entry.subnetmask = opts.subnetmask
+ if opts.broadcast:
+ entry.broadcast = opts.broadcast
+ if opts.gateway:
+ entry.gateway = opts.gateway
+ if opts.leasetime:
+ entry.default_lease = str(opts.leasetime[0])
+ entry.max_lease = str(opts.leasetime[1])
+
+ conf.add(entry)
+
+# --------------------------- REMOVE --------------------------- #
+
+def remove(opts, conf):
+ """Removes an instance of DHCPConfEntry from a DHCPConf object, returns
+ True if entry existed or False if already not present"""
+
+ # NOTE: in this implementation, hostname, MAC and IP are assumed unique
+ # TODO: could therefore make --remove able to take any one of those, not
+ # just IP address.
+ # Currently, IP address is most convenient for caller...
+
+ entry = DHCPConfEntry()
+ entry.ip = opts.ipaddress
+ return conf.remove(entry)
+
+# --------------------------- WRITE --------------------------- #
+
+def write(opts, conf):
+ """Writes out a DHCPConf object"""
+ f = None
+ try:
+ f = open(opts.policyfile, "w")
+ conf.write(f, opts.veryverbose)
+ finally:
+ if f:
+ f.close()
+
+# -------------------------- ISC DHCP ------------------------- #
+
+class ISCDHCPConf(DHCPConf):
+ def __init__(self, original_content):
+ DHCPConf.__init__(self, original_content)
+ self.hostmatch_re = re.compile(r"^host (.*) {$")
+ self.mac_re = re.compile(r"[ ]*hardware ethernet (.*);")
+ self.ip_re = re.compile(r"[ ]*fixed-address (.*);")
+ self.defaultlease_re = re.compile(r"[ ]*default-lease-time (.*);")
+ self.maxlease_re = re.compile(r"[ ]*max-lease-time (.*);")
+ self.gateway_re = re.compile(r"[ ]*option routers (.*);")
+ self.broadcast_re = re.compile(r"[ ]*option broadcast-address (.*);")
+ self.subnet_re = re.compile(r"[ ]*option subnet-mask (.*);")
+ self.dns_re = re.compile(r"[ ]*option domain-name-servers (.*);")
+ self.comment_re = re.compile(r"[ ]*# (.*)")
+
+ def parse(self, verbose):
+ # find edit point
+ lineno = 0
+ lineidx = -1
+ for line in self.original_content:
+ self.header.append(line)
+ if line.find("DHCP-CONFIG-AUTOMATIC-BEGINS") != -1:
+ lineidx = lineno
+ self.header.append("\n")
+ break
+ lineno += 1
+
+ if lineidx == -1:
+ raise Exception("cannot find edit marker in dhcp conf contents")
+
+ curr = None
+ for line in self.original_content[lineidx:]:
+ if line.startswith("host"):
+ curr = DHCPConfEntry()
+
+ if curr:
+ # Not checking validity of previously written entries.
+ # Items do not need to be ordered in entry (though they
+ # are as a side effect of the alter implementation).
+ match = self.hostmatch_re.match(line)
+ if match:
+ # option host-name generated from this as well
+ curr.hostname = match.group(1)
+ if verbose:
+ log.debug("found hostname = %s" % curr.hostname)
+ continue
+ match = self.mac_re.match(line)
+ if match:
+ curr.mac = match.group(1)
+ if verbose:
+ log.debug(" mac = %s" % curr.mac)
+ continue
+ match = self.ip_re.match(line)
+ if match:
+ curr.ip = match.group(1)
+ if verbose:
+ log.debug(" ip = %s" % curr.ip)
+ continue
+ match = self.defaultlease_re.match(line)
+ if match:
+ curr.default_lease = match.group(1)
+ if verbose:
+ log.debug(" def lease = %s" % curr.default_lease)
+ continue
+ match = self.maxlease_re.match(line)
+ if match:
+ curr.max_lease = match.group(1)
+ if verbose:
+ log.debug(" max lease = %s" % curr.max_lease)
+ continue
+ match = self.gateway_re.match(line)
+ if match:
+ curr.gateway = match.group(1)
+ if verbose:
+ log.debug(" gateway = %s" % curr.gateway)
+ continue
+ match = self.broadcast_re.match(line)
+ if match:
+ curr.broadcast = match.group(1)
+ if verbose:
+ log.debug(" broadcast = %s" % curr.broadcast)
+ continue
+ match = self.subnet_re.match(line)
+ if match:
+ curr.subnet = match.group(1)
+ if verbose:
+ log.debug(" subnet = %s" % curr.subnet)
+ continue
+ match = self.comment_re.match(line)
+ if match:
+ curr.comment = match.group(1)
+ if verbose:
+ log.debug(" comment = %s" % curr.comment)
+ continue
+ match = self.dns_re.match(line)
+ if match:
+ dnsstr = match.group(1)
+ curr.dns = dnsstr.split(" ")
+ if verbose:
+ log.debug(" dns = %s" % curr.dns)
+ continue
+
+ if line.startswith("}"):
+ if self.all_hosts.has_key(curr.ip):
+ raise Exception("duplicate IP found in conf file, aborting")
+ self.all_hosts[curr.ip] = curr
+ log.debug("found entry with IP = %s" % curr.ip)
+ curr = None
+
+ def write(self, f, verbose):
+ if not f:
+ raise Exception("no file object to write to")
+ for line in self.header:
+ f.write(line)
+ for ip in self.all_hosts:
+ self.write_entry(f, self.all_hosts[ip], verbose)
+
+ def write_entry(self, f, entry, verbose):
+ entrystr = ""
+ if not entry.hostname:
+ raise Exception("hostname required")
+ else:
+ entrystr += "host %s {\n" % entry.hostname
+
+ if entry.comment:
+ entrystr += " # %s\n" % entry.comment
+
+ if not entry.mac:
+ raise Exception("mac required")
+ else:
+ entrystr += " hardware ethernet %s;\n" % entry.mac
+
+ if not entry.ip:
+ raise Exception("ip required")
+ else:
+ entrystr += " fixed-address %s;\n" % entry.ip
+
+ if entry.default_lease:
+ entrystr += " default-lease-time %s;\n" % entry.default_lease
+
+ if entry.default_lease:
+ entrystr += " max-lease-time %s;\n" % entry.max_lease
+
+ # entry.hostname existence already checked above
+ entrystr += ' option host-name "%s";\n' % entry.hostname
+
+ if entry.subnetmask:
+ entrystr += " option subnet-mask %s;\n" % entry.subnetmask
+
+ if entry.gateway:
+ entrystr += " option routers %s;\n" % entry.gateway
+
+ if entry.broadcast:
+ entrystr += " option broadcast-address %s;\n" % entry.broadcast
+
+ if entry.dns:
+ entrystr += " option domain-name-servers"
+ for server in entry.dns:
+ entrystr += " %s" % server
+ entrystr += ";\n"
+
+ entrystr += "}\n\n"
+
+ if verbose:
+ log.debug("\n\nWRITING:\n%s" % entrystr)
+
+ f.write(entrystr)
+
+# ---------------------- VALIDATE INPUTS ---------------------- #
+
+def validate(opts):
+
+ actions = [opts.add, opts.remove]
+
+ count = 0
+ for action in actions:
+ if action:
+ count += 1
+
+ if not count:
+ raise InvalidInput("You must supply an action, see help (-h).")
+
+ if count != 1:
+ raise InvalidInput("You may only supply one action, see help (-h).")
+
+ if opts.add:
+ log.debug("--- action = add")
+ elif opts.remove:
+ log.debug("--- action = remove")
+
+ if not opts.ipaddress:
+ raise InvalidInput("You must supply an IP address, see help (-h).")
+
+ if not IPRE.match(opts.ipaddress):
+ raise InvalidInput("IP is not a valid address: %s" % opts.ipaddress)
+
+ log.debug(" ipaddress = %s" % opts.ipaddress)
+
+ if not opts.policyfile:
+ raise InvalidInput("You must supply a DHCP configuration file to alter, see help (-h).")
+
+ if not os.path.isabs(opts.policyfile):
+ raise InvalidInput("You must specify the DHCP configuration file with an absolute path.")
+ else:
+ opts.policyfile = os.path.realpath(opts.policyfile)
+ log.debug(" policyfile = %s" % opts.policyfile)
+
+ if opts.add:
+ if not opts.macaddress:
+ raise InvalidInput("Add requires a MAC address, see help (-h).")
+ else:
+ if not MACRE.match(opts.macaddress):
+ raise InvalidInput("MAC is not a valid address: %s" % opts.macaddress)
+ log.debug(" macaddress = %s" % opts.macaddress)
+ if not opts.hostname or opts.hostname.lower().strip() == 'none':
+ raise InvalidInput("Add requires a hostname, see help (-h).")
+ else:
+ log.debug(" hostname = %s" % opts.hostname)
+
+
+ if opts.lockfilepath:
+ if not os.path.isabs(opts.lockfilepath):
+ raise InvalidInput("You must specify the lockfile with an absolute path.")
+ else:
+ opts.lockfilepath = os.path.realpath(opts.lockfilepath)
+ log.debug(" lockfile = %s" % opts.lockfilepath)
+
+ if opts.subnetmask:
+ if not IPRE.match(opts.subnetmask):
+ raise InvalidInput("subnet is not a valid address (note that /# addresses are not supported yet): %s" % opts.subnetmask)
+ log.debug(" subnetmask = %s" % opts.subnetmask)
+
+ if opts.broadcast:
+ if not IPRE.match(opts.broadcast):
+ raise InvalidInput("broadcast is not a valid address: %s" % opts.broadcast)
+ log.debug(" broadcast = %s" % opts.broadcast)
+
+ if opts.gateway:
+ if not IPRE.match(opts.gateway):
+ raise InvalidInput("gateway is not a valid address: %s" % opts.gateway)
+ log.debug(" gateway = %s" % opts.gateway)
+
+ # list of dns IPs
+ opts.dns = []
+ if opts.dnsstr:
+ log.debug(" supplied dns servers: %s" % opts.dnsstr)
+ dnsservers = opts.dnsstr.split(",")
+ allvalid = True
+ for server in dnsservers:
+ if not IPRE.match(server):
+ allvalid = False
+ log.error("DNS server is not a valid address: %s" % server)
+ else:
+ opts.dns.append(server)
+ if not allvalid:
+ raise InvalidInput("all DNS servers must be valid IPs")
+ log.debug(" validated dns servers = %s" % opts.dns)
+
+ # [default_lease_time, max_lease_time]
+ opts.leasetime = []
+ if opts.leasetimestr:
+ log.debug(" supplied leasetimes: %s" % opts.leasetimestr)
+ times = opts.leasetimestr.split(",")
+ if len(times) != 2:
+ raise InvalidInput("only supply two lease times: default and max")
+ try:
+ x = int(times[0])
+ if x < 1:
+ raise InvalidInput("default lease time is less than one")
+ y = int(times[1])
+ if y < 1:
+ raise InvalidInput("max lease time is less than one")
+ if x > y:
+ raise InvalidInput("max lease time is greater than default...")
+ opts.leasetime.append(x)
+ opts.leasetime.append(y)
+ except ValueError:
+ raise InvalidInput("a lease time is not an integer")
+
+ log.debug(" default lease time = %d" % opts.leasetime[0])
+ log.debug(" max lease time = %d" % opts.leasetime[1])
+
+# --------------------------- LOCKING -------------------------- #
+
+def lock(opts):
+
+ opts.lockfile = None
+ lockfilepath = None
+
+ # validate() checked if abs path already
+ if opts.lockfilepath:
+ lockfilepath = opts.lockfilepath
+ else:
+ (confdir, f) = os.path.split(opts.policyfile)
+ lockfilepath = os.path.join(confdir, "%s%s" % (f, LOCKSUFFIX))
+
+ log.debug("about to lock, lockfile = %s" % lockfilepath)
+
+ if not os.path.exists(lockfilepath):
+ f = open(lockfilepath, "w")
+ f.write("\n")
+ f.close()
+
+ opts.lockfile = open(lockfilepath, "r+")
+ fcntl.flock(opts.lockfile.fileno(), fcntl.LOCK_EX)
+ log.debug("acquired lock")
+
+def unlock(opts):
+ if not opts.lockfile:
+ raise Exception("unlocking without a lock")
+
+ opts.lockfile.close()
+ log.debug("lock released")
+
+
+
+# ----------------------- ARGPARSE SETUP ----------------------- #
+
+def setup():
+ ver="DHCP configuration tool: %prog\nhttp://workspace.globus.org/vm/"
+ parser = optparse.OptionParser(version=ver)
+
+ parser.add_option("-q", "--quiet",
+ action="store_true", dest="quiet", default=False,
+ help="don't print any messages (unless error occurs).")
+
+ parser.add_option("-v", "--verbose",
+ action="store_true", dest="verbose", default=False,
+ help="print debug messages")
+
+ parser.add_option("-y", "--veryverbose",
+ action="store_true", dest="veryverbose", default=False,
+ help="print all debug messages")
+
+ # ----
+
+ group = optparse.OptionGroup(parser, "Required action",
+ "One of these actions is required:")
+
+ group.add_option("-a", "--add",
+ action="store_true", dest="add", default=False,
+ help="Add a policy to the DHCP configuration.")
+
+ group.add_option("-r", "--remove",
+ action="store_true", dest="remove", default=False,
+ help="Remove a policy from the DHCP configuration.")
+
+ parser.add_option_group(group)
+
+ # ----
+
+ group = optparse.OptionGroup(parser, "Required arguments",
+ "These arguments are required for both --add and --remove")
+
+ group.add_option("-p", "--policyfile", dest="policyfile", metavar="FILE",
+ help="Absolute path to the DHCP configuration file, where "
+ "the lease policies are stored")
+
+ # NOTE: in this implementation, hostname, MAC and IP are assumed unique
+ # TODO: could therefore make --remove able to take any one of those, not
+ # just IP address.
+ # Currently, IP address is most convenient for caller...
+ group.add_option("-i", "--ipaddress", dest="ipaddress", metavar="IP",
+ help="The IP address of the lease. If adding, must not "
+ "be in the policy already.")
+
+ parser.add_option_group(group)
+
+ # ----
+
+ group = optparse.OptionGroup(parser, "Required argument for --add")
+
+ group.add_option("-m", "--macaddress", dest="macaddress", metavar="MAC",
+ help="The MAC address to respond to. Must not be in the "
+ "policy already.")
+
+ group.add_option("-n", "--hostname", dest="hostname", metavar="HOST",
+ help="The hostname. Must not be in the policy already.")
+
+ parser.add_option_group(group)
+
+ # ----
+
+ group = optparse.OptionGroup(parser, "Optional arguments for --add",
+ "These arguments are optional for --add")
+
+ group.add_option("-s", "--subnetmask", dest="subnetmask", metavar="MASK",
+ help="The subnet mask (doesn't support /# form yet)")
+
+ group.add_option("-b", "--broadcast", dest="broadcast", metavar="IP",
+ help="The broadcast address")
+
+ group.add_option("-g", "--gateway", dest="gateway", metavar="IP",
+ help="The gateway address")
+
+ group.add_option("-t", "--timeforlease", dest="leasetimestr",
+ metavar="TIME,TIME",
+ help="The default,max lease time for this lease, if not "
+ "provided, the defaults in dhcpd.conf will be used.")
+
+ group.add_option("-d", "--dns", dest="dnsstr", metavar="IP[,IP,...]",
+ help="Comma separated list of DNS IPs")
+
+ parser.add_option_group(group)
+
+ # ----
+
+ group = optparse.OptionGroup(parser, "Optional arguments")
+
+ group.add_option("-l", "--lockfile", dest="lockfilepath", metavar="FILE",
+ help="Optionally override absolute path to the default "
+ "lockfile (used to ensure only one process is editing "
+ "the DHCP configuration file at a time). The default "
+ "lockfile is the supplied --policyfile file with a "
+ "suffix of '.lock'")
+
+ parser.add_option_group(group)
+
+ return parser
+
+
+# ----------------------------- RUN ----------------------------- #
+
+def run(argv=None):
+
+ parser = setup()
+
+ if argv:
+ (opts, args) = parser.parse_args(argv[1:])
+ else:
+ (opts, args) = parser.parse_args()
+
+ if opts.verbose or opts.veryverbose:
+ log.setLevel(logging.DEBUG)
+ elif opts.quiet:
+ log.setLevel(logging.ERROR)
+ else:
+ log.setLevel(logging.INFO)
+
+ try:
+ # Validate all input
+ # only other source of InvalidInput exceptions
+ validate(opts)
+
+ # Make sure this is the only process editing the policy file
+ lock(opts)
+
+ conf = parse(opts)
+
+ if opts.add:
+ add(opts, conf)
+ write(opts, conf)
+ elif opts.remove:
+ changes = remove(opts, conf)
+ if changes:
+ pass
+ write(opts, conf)
+
+ unlock(opts)
+
+ except InvalidInput, e:
+ sys.exit("FATAL: %s" % e.msg)
+ return 1
+
+ except Exception, e:
+ # a problem we did not case for
+ log.exception("Problem:")
+ try:
+ unlock(opts)
+ except:
+ pass
+ return 2
+
+def main():
+ if os.name != 'posix':
+ sys.exit("only runs on posix systems") # because of locking mechanism
+ return run()
+
+if __name__ == "__main__":
+ sys.exit(main())
+
View
372 backend/dhcp-config.sh
@@ -0,0 +1,372 @@
+#!/bin/bash
+
+# Copyright 1999-2006 University of Chicago
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy
+# of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# For more information see: http://workspace.globus.org
+
+
+#########
+# ABOUT #
+#########
+
+# This script is called via sudo to adjust ebtables and dhcp policies.
+# Use sudo version greater or equal to 1.6.8p2
+# See: http://www.gratisoft.us/sudo/alerts/bash_functions.html
+
+# This script must be owned and writable only by root and placed in a non-
+# tamperable directory.
+
+# SYNTAX:
+
+# See the "adjust as necessary" section below.
+
+
+########
+# SUDO #
+########
+
+IFS=' '
+
+\unalias -a
+
+set -f -e -u -C -p -P
+
+# set:
+#
+# -f
+# Disable file name generation (globbing).
+# -e
+# Exit immediately if a simple command (see section Simple Commands)
+# exits with a non-zero status, unless the command that fails is part
+# of an until or while loop, part of an if statement, part of a && or
+# || list, or if the command's return status is being inverted using !
+# -u
+# Treat unset variables as an error when performing parameter expansion.
+# An error message will be written to the standard error, and a
+# non-interactive shell will exit.
+# -C
+# Prevent output redirection using `>', `>&', and `<>' from overwriting
+# existing files.
+# -p
+# Turn on privileged mode. In this mode, the $BASH_ENV and $ENV files are
+# not processed, shell functions are not inherited from the environment,
+# and the SHELLOPTS variable, if it appears in the environment, is ignored.
+# If the shell is started with the effective user (group) id not equal to
+# the real user (group) id, and the -p option is not supplied, these
+# actions are taken and the effective user id is set to the real user id.
+# If the -p option is supplied at startup, the effective user id is not
+# reset. Turning this option off causes the effective user and group ids
+# to be set to the real user and group ids.
+# -P
+# If set, do not follow symbolic links when performing commands such as cd
+# which change the current directory. The physical directory is used
+# instead. By default, Bash follows the logical chain of directories
+# when performing commands which change the current directory.
+
+# NOTE that the -e flag is why we do some odd || checks below
+
+PATH="/bin:/sbin:/usr/bin:/usr/local/bin"
+SHELL=/bin/bash
+export PATH SHELL
+
+unset -f strlen
+function strlen (){
+ eval echo "\${#${1}}"
+}
+
+
+#######################
+# ADJUST AS NECESSARY #
+#######################
+
+# Policy file for script to adjust
+#DHCPD_CONF="/etc/dhcp/dhcpd.conf"
+DHCPD_CONF="/etc/dhcpd.conf"
+
+# Command to run before policy adjustment
+DHCPD_STOP="/etc/init.d/dhcpd stop"
+
+# Command to run after policy adjustment
+DHCPD_START="/etc/init.d/dhcpd start"
+
+# Sleep between stop and start? in seconds
+SLEEP=0
+
+# If DIRNAME is not available and commented out, you must adjust these
+# files to their absolute paths. By default, $DIRNAME is used to find
+# the DHCP_CONF_ALTER and EBTABLES_CONFIG scripts, the assumption being
+# that they are in the same directory as this script (dhcp-config.sh).
+DHCP_CONF_ALTER="dhcp-conf-alter.py"
+EBTABLES_CONFIG="ebtables-config.sh"
+
+DIRNAME="dirname"
+
+function die_dirname() {
+ echo "ERROR: DIRNAME invocation failed. Suggestion: use hardcoded"
+ echo " paths to $DHCP_CONF_ALTER and $EBTABLES_CONFIG"
+ exit 1
+}
+
+if [ "X$DIRNAME" != "X" ]; then
+ # get the current directory of this script (dhcp-config.sh)
+ curdir=`$DIRNAME $0` || die_dirname
+
+ DHCP_CONF_ALTER=$curdir/$DHCP_CONF_ALTER
+ EBTABLES_CONFIG=$curdir/$EBTABLES_CONFIG
+fi
+
+# Instead of adjusting DHCP_CONF_ALTER and EBTABLES_CONFIG (if it is
+# necessary), you could also just hardcode these CMDs if you want to,
+# DHCP_CONF_ALTER and EBTABLES_CONFIG are not used again after this
+DHCP_CONF_ALTER_CMD="python $DHCP_CONF_ALTER"
+EBTABLES_CONFIG_CMD="bash $EBTABLES_CONFIG"
+
+echo "DHCP_CONF_ALTER_CMD: $DHCP_CONF_ALTER_CMD"
+echo "EBTABLES_CONFIG_CMD: $EBTABLES_CONFIG_CMD"
+
+unset DHCP_CONF_ALTER
+unset EBTABLES_CONFIG
+unset DIRNAME
+unset curdir
+
+unset PYTHONPATH
+unset PYTHONINSPECT
+
+#############
+# ARGUMENTS #
+#############
+
+if [ $# -lt 3 ]; then
+ echo "ERROR: requires at least 3 arguments. Syntax: add|rem <vifname> <ipaddr> [<dhcpif> <macaddr> <broadcast> <subnetmask> <gateway> <hostname>]"
+ exit 1
+fi
+
+ADDREM=$1
+echo " subcommand: $ADDREM"
+VIFNAME=$2
+echo " vifname: $VIFNAME"
+IPADDR=$3
+echo " ipaddr: $IPADDR"
+
+if [ "$ADDREM" != "add" ] && [ "$ADDREM" != "rem" ]; then
+ echo "ERROR: subcommand must be 'add' or 'rem'"
+ exit 1
+fi
+
+if [ "$ADDREM" = "rem" ] && [ $# -ne 3 ]; then
+ echo "ERROR: rem requires just 3 arguments: rem <vifname> <ipaddr>"
+ exit 1
+fi
+
+if [ "$ADDREM" = "add" ]; then
+ if [ $# -ne 11 ]; then
+ echo "ERROR: add requires 11 arguments: add <vifname> <ipaddr> <dhcpif> <macaddr> <broadcast> <subnetmask> <gateway> <hostname> <dns1> <dns2>"
+ echo "(These can be 'none': broadcast, subnetmask, gateway, dns1, dns2)"
+ exit 1
+ else
+ DHCPIF=$4
+ echo " dhcpif: $DHCPIF"
+ MACADDR=$5
+ echo " macaddr: $MACADDR"
+ BROADCAST=$6
+ echo " broadcast: $BROADCAST"
+ SUBNETMASK=$7
+ echo " subnetmask: $SUBNETMASK"
+ GATEWAY=$8
+ echo " gateway: $GATEWAY"
+ HOSTNAME=$9
+ echo " hostname: $HOSTNAME"
+ shift
+ DNS1=$9
+ echo " dns1: $DNS1"
+ shift
+ DNS2=$9
+ echo " dns2: $DNS2"
+ fi
+fi
+
+#############################
+#### EBTABLES ADJUSTMENT ####
+#############################
+
+SUCCESS="y"
+
+if [ "$ADDREM" = "rem" ]; then
+ echo "CMD: $EBTABLES_CONFIG_CMD rem $VIFNAME"
+ $EBTABLES_CONFIG_CMD rem $VIFNAME || SUCCESS="n"
+ if [ "$SUCCESS" != "y" ]; then
+ echo "ERROR: ebtables remove failed"
+ else
+ echo "ebtables remove succeeded"
+ fi
+fi
+
+function die_ebtables() {
+ echo "ERROR: ebtables addition failed"
+ exit 1
+}
+
+if [ "$ADDREM" = "add" ]; then
+ echo "CMD: $EBTABLES_CONFIG_CMD add $VIFNAME $DHCPIF $MACADDR $IPADDR"
+ $EBTABLES_CONFIG_CMD add $VIFNAME $DHCPIF $MACADDR $IPADDR || die_ebtables
+ echo "ebtables addition succeeded"
+fi
+
+##########################
+#### DHCPD ADJUSTMENT ####
+##########################
+
+function die_dhcpd_stop_ok() {
+ echo "ERROR: dhcp stop failed (ok)"
+}
+
+function die_dhcpd_remove() {
+ echo "ERROR: dhcp policy remove failed"
+ SUCCESS="n"
+}
+
+function die_dhcpd_start() {
+ echo "ERROR: dhcp start failed"
+ SUCCESS="n"
+}
+
+function die_dhcpd_alter() {
+ echo "ERROR: dhcp policy addition failed"
+ SUCCESS="n" # still restart
+}
+
+# LOCKING
+
+TEMPFILE="$0.$$"
+LOCKFILE="$0.lock"
+
+set +e
+
+function my_lockfile ()
+{
+ echo $$ > $TEMPFILE || {
+ echo "You don't have permission to access `dirname $TEMPFILE`"
+ return 1
+ }
+
+ ln $TEMPFILE $LOCKFILE && {
+ rm -f $TEMPFILE
+ return 0
+ }
+
+ rm -f $TEMPFILE
+ return 1
+}
+
+function stale_check ()
+{
+ PID=""
+ PID=`cat $LOCKFILE`
+ if [ "X$PID" != "X" ]; then
+ echo "checking if LOCKFILE pid $PID is stale"
+ kill -0 $PID
+ if [ $? != 0 ]; then
+ echo "pid $PID does not exist, removing lockfile"
+ rm -f $LOCKFILE
+ fi
+ fi
+}
+
+# wait for a lock
+num=5
+until my_lockfile ; do
+ sleep 0.1
+
+ num=`expr $num - 1`
+ if [ $num = 0 ]; then
+ stale_check
+ num=5
+ fi
+done
+
+
+if [ "$ADDREM" = "rem" ]; then
+ echo "CMD: $DHCPD_STOP"
+ $DHCPD_STOP || die_dhcpd_stop_ok
+
+ echo "CMD: $DHCP_CONF_ALTER_CMD -r -i $IPADDR -p $DHCPD_CONF"
+ x=1
+ $DHCP_CONF_ALTER_CMD -r -i $IPADDR -p $DHCPD_CONF && x=0 || die_dhcpd_remove
+ if [ $x -eq 0 ]; then
+ echo "dhcp policy remove succeeded"
+ fi
+
+ if [ "$SLEEP" -ne "0" ]; then
+ echo "Sleeping for $SLEEP seconds."
+ sleep $SLEEP
+ fi
+
+ echo "CMD: $DHCPD_START"
+ $DHCPD_START || die_dhcpd_start
+fi
+
+
+if [ "$ADDREM" = "add" ]; then
+ ARGS="-a -i $IPADDR -m $MACADDR -n $HOSTNAME -p $DHCPD_CONF"
+
+ if [ "$BROADCAST" != "none" ]; then
+ ARGS="$ARGS -b $BROADCAST"
+ fi
+
+ if [ "$SUBNETMASK" != "none" ]; then
+ ARGS="$ARGS -s $SUBNETMASK"
+ fi
+
+ if [ "$GATEWAY" != "none" ]; then
+ ARGS="$ARGS -g $GATEWAY"
+ fi
+
+ if [ "$DNS1" != "none" ] && [ "$DNS2" != "none" ]; then
+ ARGS="$ARGS -d $DNS1,$DNS2"
+ else
+ if [ "$DNS1" != "none" ]; then
+ ARGS="$ARGS -d $DNS1"
+ fi
+ if [ "$DNS2" != "none" ]; then
+ ARGS="$ARGS -d $DNS2"
+ fi
+ fi
+
+ echo "CMD: $DHCPD_STOP"
+ $DHCPD_STOP || die_dhcpd_stop_ok
+
+ echo "CMD: $DHCP_CONF_ALTER_CMD $ARGS"
+
+ x=1
+ $DHCP_CONF_ALTER_CMD $ARGS && x=0 || die_dhcpd_alter
+ if [ $x -eq 0 ]; then
+ echo "dhcp policy addition succeeded"
+ fi
+
+ if [ "$SLEEP" -ne "0" ]; then
+ echo "Sleeping for $SLEEP seconds."
+ sleep $SLEEP
+ fi
+
+ echo "CMD: $DHCPD_START"
+ $DHCPD_START || die_dhcpd_start
+fi
+
+rm -f $LOCKFILE
+
+if [ "$SUCCESS" = "n" ]; then
+ exit 1
+fi
+
View
64 backend/dhcpd.conf.example
@@ -0,0 +1,64 @@
+# dhcpd.conf
+#
+# Configuration file for ISC dhcpd for workspaces
+
+
+#################
+## GLOBAL OPTS ##
+#################
+
+# Option definitions common or default to all supported networks
+
+# Keep this:
+ddns-update-style none;
+
+# Can be overriden in host entry:
+default-lease-time 120;
+max-lease-time 240;
+
+
+#############
+## SUBNETS ##
+#############
+
+# Make an entry like this for each supported subnet. Otherwise, the DHCP
+# daemon will not listen for requests on the interface of that subnet.
+
+subnet 192.168.0.0 netmask 255.255.255.0 {
+}
+
+subnet 172.16.65.0 netmask 255.255.255.0 {
+}
+
+
+# For multiple networks off the same (local) interface, comment out the
+# above and use the following construct:
+
+#shared-network networks_ether1 {
+#
+# subnet 10.135.125.0 netmask 255.255.255.0 {
+# }
+#
+# subnet 172.22.0.0 netmask 255.255.0.0 {
+# }
+#}
+
+# Most DHCP servers will require an actual IP presence on these other
+# networks.
+#
+# For example, consider the local eth0 has this address: 10.135.125.17.
+# To support the other network 172.22.0.0/16, make an eth0 interface alias
+# like this: "ip link set veth1 name eth0:0".
+#
+# Then assign it an IP on the other network, for example:
+# "ifconfig eth0:0 172.22.0.1 netmask 255.255.0.0"
+# Note this IP cannot be one that is leased to a VM in the workspace
+# service network pool configurations.
+
+
+### DO NOT EDIT BELOW, the following entries are added and
+### removed programmatically.
+
+### DHCP-CONFIG-AUTOMATIC-BEGINS ###
+
+
View
248 backend/ebtables-config.sh
@@ -0,0 +1,248 @@
+#!/bin/bash
+
+# Copyright 1999-2006 University of Chicago
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy
+# of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# For more information see: http://workspace.globus.org
+
+
+#########
+# ABOUT #
+#########
+
+# This script adjusts ebtables rules to stop DHCP packets from workspaces
+# escaping to the site network and to prevent MAC and IP spoofing.
+
+# 1. Is the packet coming from a workspace virtual interface?
+# 2. If not, proceed without further processing.
+# 3. If so, is the MAC address incorrect? Drop the packet.
+# 4. Is this is a DHCP packet?
+# 5. If so, allow it to be bridged only to the appropriate interface
+# for the bridge that it is on. No other interface on the bridge
+# will see the broadcast packet.
+# 6. If not a DHCP packet, it must have the correct source IP address,
+# otherwise the packet is dropped.
+
+
+# If you are running a site network DHCP server (and even if not it is good
+# practice), you should add an ebtables rule to each system that drops any DHCP
+# request packets coming from the peth* interfaces.
+# TODO: example rule
+
+
+#######################
+# ADJUST AS NECESSARY #
+#######################
+
+#EBTABLES=/sbin/ebtables
+EBTABLES=/usr/sbin/ebtables
+
+# to test a dryrun, replace EBTABLES with a script that just prints args
+# (e.g., echo "$@")
+
+
+#############
+# ARGUMENTS #
+#############
+
+if [ $# -lt 2 ]; then
+ echo "ERROR: requires at least 2 arguments, syntax: add|rem <vifname> [<dhcpif> <macaddr> <ipaddr>]"
+ exit 1
+fi
+
+ADDREM=$1
+echo " subcommand: $ADDREM"
+VIFNAME=$2
+echo " vifname: $VIFNAME"
+
+if [ "$ADDREM" != "add" ] && [ "$ADDREM" != "rem" ]; then
+ echo "ERROR: subcommand must be 'add' or 'rem'"
+ exit 1
+fi
+
+if [ "$ADDREM" = "add" ]; then
+ if [ $# -ne 5 ]; then
+ echo "ERROR: add requires 5 arguments: add <vifname> <dhcpif> <macaddr> <ipaddr>"
+ exit 1
+ else
+ DHCPIF=$3
+ echo " dhcpif: $DHCPIF"
+ MACADDR=$4
+ echo " macaddr: $MACADDR"
+ IPADDR=$5
+ echo " ipaddr: $IPADDR"
+ fi
+fi
+
+if [ "$ADDREM" = "rem" ] && [ $# -ne 2 ]; then
+ echo "ERROR: rem requires just 2 arguments: rem <vifname>"
+ exit 1
+fi
+
+
+###################################
+#### EBTABLES CHAINS AND RULES ####
+###################################
+
+
+# function init_dhcpif_chain
+#
+# This chain is the target for any workspace on the bridge that is being
+# served by a DHCP server in dom0 with a listening interface on the subnet.
+#
+# The overall logic is: if this is a workspace NIC and if this is a DHCP
+# request (these two truths are the only way to get packets to this chain),
+# then send it only to the right dom0 interface (or another dom if the dhcp
+# server is running in another domain). DROP every other port on the
+# bridge, the DHCP request will not go to any other workspace or out
+# to the site's network.
+
+function init_dhcpif_chain() {
+ $EBTABLES -N DHCP-$DHCPIF
+ if [ $? -ne 0 ]; then
+ return 1
+ fi
+ $EBTABLES -A DHCP-$DHCPIF -o $DHCPIF -j ACCEPT
+ if [ $? -ne 0 ]; then
+ echo "ERROR: could not add $DHCPIF -o rule"
+ return 2
+ fi
+ $EBTABLES -P DHCP-$DHCPIF DROP
+ if [ $? -ne 0 ]; then
+ echo "ERROR: could not set default policy for DHCP-$DHCPIF chain"
+ return 2
+ fi
+ return 0
+}
+
+
+# function init_vifname_chain
+#
+# This chain is the target that all packets from a workspace NIC are
+# sent. All packets must have the correct source MAC address. Any
+# DHCP request must be sent to the appropriate dhcpif chain. If it
+# is not a DHCP request, it must have the correct source IP address.
+
+function init_vifname_chain() {
+ $EBTABLES -N $VIFNAME
+ if [ $? -ne 0 ]; then
+ echo "ERROR: could not create $VIFNAME chain"
+ return 1
+ fi
+
+ $EBTABLES -P $VIFNAME ACCEPT
+ if [ $? -ne 0 ]; then
+ echo "ERROR: could not set default policy for $VIFNAME chain"
+ return 1
+ fi
+
+ # First make sure it is using the right MAC address
+ $EBTABLES -A $VIFNAME -s ! $MACADDR -j DROP
+ if [ $? -ne 0 ]; then
+ echo "ERROR: could not set MAC address policy for $VIFNAME chain"
+ return 1
+ fi
+
+ # If this is a DHCP request, send it to the right handler
+ $EBTABLES -A $VIFNAME -p IPv4 --ip-proto 17 --ip-dport 67 -j DHCP-$DHCPIF
+ if [ $? -ne 0 ]; then
+ echo "ERROR: could not set DHCP policy for $VIFNAME chain"
+ return 1
+ fi
+
+ # Make sure it using the right IP address (this needs to be after the
+ # DHCP handler branch since those packets do not have IPs yet).
+ $EBTABLES -A $VIFNAME -p IPv4 --ip-src ! $IPADDR -j DROP
+ if [ $? -ne 0 ]; then
+ echo "ERROR: could not set IP policy for $VIFNAME chain"
+ return 1
+ fi
+
+ return 0
+}
+
+
+function delete_vifname_chain() {
+ $EBTABLES -X $VIFNAME
+ return $?
+}
+
+function add_forward_rule() {
+ $EBTABLES -A FORWARD -i $VIFNAME -j $VIFNAME
+ return $?
+}
+
+function rem_forward_rule() {
+ $EBTABLES -D FORWARD -i $VIFNAME -j $VIFNAME
+ return $?
+}
+
+
+##########################
+#### SUBCOMMAND IMPLS ####
+##########################
+
+if [ "$ADDREM" = "rem" ]; then
+
+ SUCCESS="y"
+ rem_forward_rule
+ if [ $? -ne 0 ]; then
+ echo "ERROR: Failed to remove $VIFNAME FORWARD rule"
+ SUCCESS="n"
+ else
+ echo "Removed $VIFNAME FORWARD rule"
+ fi
+
+ delete_vifname_chain
+ if [ $? -ne 0 ]; then
+ echo "ERROR: Failed to delete $VIFNAME chain"
+ SUCCESS="n"
+ else
+ echo "Deleted $VIFNAME chain"
+ fi
+
+ if [ "$SUCCESS" = "n" ]; then
+ exit 1
+ fi
+ exit 0
+fi
+
+
+if [ "$ADDREM" = "add" ]; then
+
+ # error 1 is ignored, DHCPIF chain in place already (could write
+ # func to test for that). If there is a general ebtables problem
+ # func after this will also fail.
+ init_dhcpif_chain
+ if [ $? -eq 2 ]; then
+ echo "ERROR: could not create $DHCPIF chain"
+ exit 1
+ fi
+
+ init_vifname_chain
+ if [ $? -ne 0 ]; then
+ exit 1
+ else
+ echo "Created $VIFNAME chain"
+ fi
+
+ add_forward_rule
+ if [ $? -ne 0 ]; then
+ echo "ERROR: Failed to add $VIFNAME FORWARD rule"
+ exit 1
+ else
+ echo "Added $VIFNAME FORWARD rule"
+ exit 0
+ fi
+fi
View
1,394 backend/install.py
@@ -0,0 +1,1394 @@
+#!/usr/bin/env python
+
+# Copyright 1999-2006 University of Chicago
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy
+# of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+# For more information see: http://workspace.globus.org
+
+import ConfigParser
+import fileinput
+import grp
+import logging
+import optparse
+import os
+import pwd
+import shutil
+import stat
+import sys
+from zipfile import PyZipFile
+
+PERM_NOTE="""
+sudo is used for altering/securing the system's networking setup for a VM
+
+And for Xen3, it is used for xm and xend.
+
+Corresponding entries must be in the sudoers file, e.g:
+
+ globus ALL=(root) NOPASSWD: /opt/workspace/bin/dhcp-config.sh
+ globus ALL=(root) NOPASSWD: /usr/sbin/xm
+ globus ALL=(root) NOPASSWD: /usr/sbin/xend
+
+... where globus is the account that workspace-control is run under
+
+For information about sudo, see:
+ http://www.gratisoft.us/sudo/
+And:
+ http://xkcd.com/c149.html
+
+Since we call it via sudo, the default dhcpconfig value
+"/opt/workspace/bin/dhcp-config.sh" implies that /opt, /opt/workspace/,
+and /opt/workspace/bin are not writeable by anyone but root.
+
+"/opt/workspace/bin/dhcp-config.sh" itself should be owned by
+root and chmod 700
+
+These are the recommended permissions for the default
+/opt/workspace configurations distributed in the program's
+example configuration file:
+
+drwxr-xr-x root root /opt
+drwxr-xr-x root root /opt/workspace
+
+/opt/workspace contents:
+
+drwxr-x--- root globus bin
+drwxr-x--- root globus images
+drwx------ globus globus logs
+drwx------ globus globus persistence
+drwx------ globus globus secureimages
+-rw-r----- root globus worksp.conf
+
+This installer can accomplish all of this for you.
+"""
+
+
+ZIPHEADER="""#!/bin/sh
+exec python -c '
+import sys
+assert sys.version >= "2.3", sys.argv[1] + ": Python 2.3 or greater required"
+sys.path.insert(0, sys.argv[1])
+del sys.argv[0]
+import worksp
+sys.exit(worksp.main())
+' "$0" "$@"
+exit 1
+"""
+
+log = logging.getLogger("worksp")
+ch = logging.StreamHandler()
+ch.setLevel(logging.DEBUG)
+
+formatter = logging.Formatter("%(levelname)s: %(message)s")
+
+# for timestamp and line no
+#formatter = logging.Formatter("%(asctime)s (%(lineno)d) %(levelname)s - %(message)s")
+
+ch.setFormatter(formatter)
+log.addHandler(ch)
+
+class StopInstallation(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+ def __str__(self):
+ return self.msg
+
+class StopInstallationError(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+ def __str__(self):
+ return self.msg
+
+class NoValue(Exception):
+ pass
+
+def confValResolve(section, key, config, family):
+ """Resolves values from overriding sections of configuration file"""
+
+ result = None
+
+ # First try family
+ if family:
+ try:
+ result = config.get(section+"-"+family,key)
+ return result
+ except:
+ pass
+ # If not in family specific section, try regular section
+ try:
+ result = config.get(section,key)
+ return result
+ except:
+ raise NoValue()
+
+def curry(func, *args, **kwds):
+ def callit(*moreargs, **morekwds):
+ kw = kwds.copy()
+ kw.update(morekwds)
+ return func(*(moreargs+args), **kw)
+ return callit
+
+def initGetVal(conf):
+ return curry(confValResolve, conf, conf.family)
+
+def userBooleanQ(querystring, default="Y"):
+ resp = None
+ if default == "Y":
+ tag = " [Y/n]: "
+ else:
+ tag = " [y/N]: "
+ while (resp == None):
+ resp = raw_input(querystring + tag)
+ if resp == None:
+ continue
+ if resp == "":
+ if default == "Y":
+ return True
+ else:
+ return False
+
+ resp = resp.strip().lower()
+ if (resp[0] == 'y'):
+ return True
+ elif (resp[0] == 'n'):
+ return False
+ else:
+ print "(invalid response)"
+ resp = None
+
+def userStringQ(querystring):
+ return raw_input(querystring)
+
+def userDirChangeQ(path, mode, userstr, groupstr, default="Y"):
+ q = "\nAlter '%s' to %s?" % (path, modeStr(mode))
+ return userBooleanQ(q, default)
+
+def modeStr(mode):
+ string=""
+ mode=stat.S_IMODE(mode)
+ for i in ("USR", "GRP", "OTH"):
+ for perm in "R", "W", "X":
+ if mode & getattr(stat, "S_I"+ perm + i):
+ string = string + perm.lower()
+ else:
+ string = string + "-"
+ return string
+
+def mode700(mode):
+ mode = stat.S_IMODE(mode)
+ if not mode & stat.S_IRWXU:
+ return False
+ for i in ("GRP", "OTH"):
+ for perm in "R", "W", "X":
+ if mode & getattr(stat, "S_I"+ perm + i):
+ return False
+ return True
+
+def mode750(mode):
+ mode = stat.S_IMODE(mode)
+ for perm in (stat.S_IRWXU, stat.S_IRGRP, stat.S_IXGRP):
+ if not mode & perm:
+ return False
+ for perm in (stat.S_IWGRP, stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH):
+ if mode & perm: