Index: /trunk/AUTHORS
===================================================================
--- /trunk/AUTHORS	(revision 132)
+++ /trunk/AUTHORS	(revision 133)
@@ -16,2 +16,5 @@
 Ole Holm Nielsen <Ole.H.Nielsen at fysik dot dtu dot dk>
 
+Advance Parser implementation
+========================================
+Dennis Stam
Index: /trunk/Changelog
===================================================================
--- /trunk/Changelog	(revision 132)
+++ /trunk/Changelog	(revision 133)
@@ -1,2 +1,8 @@
+3.0.0:
+ - Added the Advanced Parser module created by Dennis Stam. This
+   Removed a lot of obsolete code and command line options. The new version 
+   is not compatible with the previous one.
+   Author: Bas van der Vlies & Dennis Stam
+
 2.0.0
  - Fixed a bug in -r/--remove option. Do not display
Index: /trunk/debian/changelog
===================================================================
--- /trunk/debian/changelog	(revision 132)
+++ /trunk/debian/changelog	(revision 133)
@@ -1,2 +1,8 @@
+pxeconfig (2.0.1-1) stable; urgency=low
+
+  * fixed a typo in help function
+
+ -- root <root@gb-ldapn1.irc.sara.nl.irc.sara.nl>  Mon, 19 Jan 2009 16:57:41 +0100
+
 pxeconfig (2.0.0-1) stable; urgency=low
 
Index: /trunk/pxeconfig.in
===================================================================
--- /trunk/pxeconfig.in	(revision 132)
+++ /trunk/pxeconfig.in	(revision 133)
@@ -30,30 +30,23 @@
 #
 """
-Usage: pxeconfig 
-
- -V,--version
- [-r,--remove] -i,--interactive
- [-r,--remove] -f,--file <filename> <hostname(s)>
- [-r,--remove] -n,--net <C-net> -s,--start <number> -e,--end <number> -f,--file <filename>
-
-There are several ways to create a range of hostnames. They all start with:
- * -b,--basename <string>
-
-The different formats are:
-  1  [opt] -H,--host-range <number>-<number> -f,--file <filename>
-  2  [opt] -s,--start <number> -e,--end <number> -f,--file <filename>
-  3  [opt] -R,--rack <string>-<string>  -N,--node <string>-<string> -f,--file <filename>
-
-opt:
- -r,--remove 
- -w,--equal-width 
- --dry-run
-
-If leading zeros are used for <number>, then host names will be padded 
-with zeros. --equal-width option will be enabled.
+Usage: pxeconfig [-f,--filename <name>] <hosts>
+
+Specifying hostnames:
+   To specify a range use the [] to indicate a range,
+   a couple of examples herebelow.
+
+   The first five nodes of rack 16
+	  - gb-r16n[1-5]
+
+   The first five nodes and node 12 and 18 of rack 16 to 20
+      - gb-r[16-20]n[1-5,12,18]
+
+   The first five nodes de in rack 16 with a padding enabled 
+	  - gb-r[16]n[01-5]
+
+The ranges ([]) are not only limited to numbers, letters can also be used.
 
 With this program you can configure which PXE configuration file a node
-will use when it boots. The program can be started interactivly or via
-command line options.
+will use when it boots.
 
 See following link for usage and examples:
@@ -73,42 +66,11 @@
 from sara import AdvancedParser
 
-# Global Variables
-#
-DEBUG   = False
-VERBOSE = False
-DRY_RUN = False
-
 # Constants
 #
-PXE_CONF_DIR='/tftpboot/pxelinux.cfg'
-NETWORK='network'
-BASENAME='basename'
-FILENAME='filename'
-START='start'
-END='end'
-REMOVE='remove'
-RACK='rack'
-NODE='node'
-INTERACTIVE='interactive'
-EQUALWIDTH='equalwidth'
-VERSION='2.0.0'
-
-SHORTOPT_LIST='b:e:f:hin:s:rwvN:H:R:V'
-LONGOPT_LIST=[ 'basename=', 'debug', 'dry-run', 'end=', 'equal-width', 
-    'file=', 'help', 'host-range=', 'interactive', 'net=', 'node=', 
-    'start=', 'rack=', 'remove', 'verbose', 'version', 'equal-width'
-	]
-
-# regex definition
-#
-# A regulare expression to parse string for rack/node
-id_re = re.compile(r"""
-	(?P<basename>[a-zA-Z-_]*)
-	(?P<number>[0-9]+) 
-     """, re.VERBOSE)
-
-def verbose(str):
-	if VERBOSE:
-		print '%s' %str
+PXE_CONF_DIR = '/tftpboot/pxelinux.cfg'
+NETWORK      = 'network'
+START        = 'start'
+END          = 'end'
+VERSION      = '3.0.0'
 
 class PxeConfig(Exception):
@@ -119,5 +81,4 @@
 	def __repr__(self):
 		return self.msg
-
 
 def ReadConfig(file):
@@ -148,52 +109,53 @@
 
 def select_pxe_configfile():
-  """
-  Let user choose which pxeconfig file to use.
-  """
-
-  os.chdir(PXE_CONF_DIR)
-
-  # Try to determine to which file the default symlink points, and 
-  # if it exists, remove it from the list.
-  #
-  try:
-    default_file = os.readlink('default')
-  except OSError:
-    default_file = None
-    pass
-
-  files = glob.glob('default.*')
-  if not files:
-    print 'There are no pxe config files starting with: default.'
-    sys.exit(1)
-
-  if default_file:
-    files.remove(default_file)
-
-  # sort the files
-  #
-  files.sort()
+	"""
+	Let user choose which pxeconfig file to use.
+	""" 
+	
+	os.chdir(PXE_CONF_DIR) 
+	
+	# Try to determine to which file the default symlink points, and 
+	# if it exists, remove it from the list.
+	# 
+	try:
+		default_file = os.readlink('default')
+	except OSError:
+		default_file = None
+    	pass
+
+	files = glob.glob('default.*')
+	if not files:
+		print 'There are no pxe config files starting with: default.'
+		sys.exit(1)
+
+	if default_file:
+		files.remove(default_file)
+
+  	# sort the files
+	#
+	files.sort()
  
-  print 'Which pxe config file must we use: ?' 
-  i = 1   
-  for file in files:
-    print "%d : %s" %(i,file)
-    i = i +1
-
-  while 1:
-    index = raw_input('Select a number: ')
-    try:
-      index = int(index)
-    except ValueError:
-      index = len(files) + 1
-
-    # Is the user smart enough to select
-    # the right value??
-    #
-    if 0 < index <= len(files): break
-
-  return files[index-1]
-
-def manage_links(dict):
+	print 'Which pxe config file must we use: ?' 
+	i = 1   
+	for file in files:
+		print "%d : %s" %(i,file)
+		i = i + 1
+
+	while 1:
+		index = raw_input('Select a number: ')
+		try:
+			index = int(index)
+		except ValueError:
+			index = len(files) + 1
+
+		# Is the user smart enough to select
+		# the right value??
+		#
+		if 0 < index <= len(files): 
+			break
+
+	return files[index-1]
+
+def manage_links(dict, options):
 	"""
 	Create the links in the PXE_CONF_DIR,
@@ -201,37 +163,38 @@
            start number, end number
 	"""
-	str = 'manage_links'
-	verbose(str) 
+	if options.VERBOSE:
+		print 'manage_links()'
 	
 	os.chdir(PXE_CONF_DIR)
 	naddr = dict[NETWORK]
-	pxe_filename = dict[FILENAME] 
+	pxe_filename = options.filename
 	
 	for i in range(dict[START], dict[END]+1):
 		haddr = '%s%02X' %(naddr, i) 
-		
-		if dict[REMOVE] == True:
-			if DEBUG or DRY_RUN:
+	
+		if options.REMOVE:
+			if options.DEBUG or options.DRY_RUN:
 				print 'removing %s/%s' %(PXE_CONF_DIR, haddr)
 
-			if os.path.exists(haddr) and not DRY_RUN:
+			if os.path.exists(haddr) and not options.DRY_RUN:
 				os.unlink(haddr)
 
 		else:
-			if DEBUG or DRY_RUN:
+			if options.DEBUG or options.DRY_RUN:
 				print 'linking %s to %s' %(haddr, pxe_filename)
 
-			if not DRY_RUN:
+			if not options.DRY_RUN:
 				if os.path.exists(haddr):
 					os.unlink(haddr) 
 				os.symlink(pxe_filename, haddr)
 
-def net_2_hex(net):
+def net_2_hex(net, options):
 	"""
 	This function checks if the give network is a Class C-network and will
 	convert the network address to a hex address if true.
 	"""
-	str = 'net_2_hex : %s' %(net)
-	verbose(str) 
+	if options.VERBOSE:
+		str = 'net_2_hex : %s' %(net)
+		print str
 	
 	d = string.split(net, '.') 
@@ -243,216 +206,10 @@
 	r = ''
 	for i in d:
-		i = check_number(i, True)
-		r = '%s%02X' %(r,i)
-
-	if DEBUG:
-		print 'C-network in hex: ', r
+		r = '%s%02X' %(r, int(i))
+
+	if options.DEBUG:
+		print 'C-network converted to hex: ', r
 
 	return r
-
-def check_number(number_str, network):
-	"""
-	number : a string. If string starts with a zero (0) then 
-	         EQUALWIDTH wil be set.
-	network: if true then number must ben between 0 < number < 255
-	         else it must be a valid number. 
-	"""
-	try:
-		n = int(number_str)
-	except ValueError, detail:
-		error = "%s : is not a valid number" %number_str
-		raise PxeConfig, error
-
-	if not network:
-		return n
-
-	# Check if it is a correct network value
-	#
-	if 0 <= n <= 255:
-		return n
-	else:
-		error = '%s is not a valid network number, must be between 0 and 255' %n
-		raise PxeConfig, error
-
-def interactive(binfo):
-	print __doc__ 
-	
-
-	while 1:
-		network = raw_input('Give network address (xxx.xxx.xxx): ') 
-
-		try:
-			naddr = net_2_hex(network) 
-			break
-		except PxeConfig, detail:
-			print '%s : not a valid C-class network number' %(network)
-			continue
-
-	while 1:
-		start = raw_input('Starting number: ') 
-
-		try:
-			start = check_number(start, True) 
-			break
-		except PxeConfig, detail:
-			print detail
-			continue
-		
-	while 1:
-		end = raw_input('Ending number: ') 
-
-		try:
-			end = check_number(end, True) 
-			break
-		except PxeConfig, detail:
-			print detail
-			continue
-	
-	
-	pxe_filename = select_pxe_configfile() 
-	
-	binfo[NETWORK] = naddr
-	binfo[START] = start
-	binfo[END] = end
-	binfo[FILENAME] = pxe_filename 
-	
-	if DEBUG:
-		print network, binfo
-
-	manage_links(binfo)
-
-def check_args(binfo, hostnames):
-	"""
-	Do you we have the right and propper values 
-	"""
-	### check_filename 
-	#
-	str = 'check_args: '
-	verbose(str)	
-	try:
-		if not os.path.isfile(os.path.join(PXE_CONF_DIR, binfo[FILENAME])):
-			error =  '%s: Filename does not exists' %binfo[FILENAME]
-			raise Pxeconfig, detail
-	except KeyError, detail:
-		if binfo[REMOVE] :
-			binfo[FILENAME] = 'Does not matter'
-		else:
-			binfo[FILENAME] = select_pxe_configfile() 
-
-	if hostnames:
-		host_2_hex(hostnames, binfo)
-		sys.exit(0)
-		
-	if binfo.has_key(BASENAME) and binfo.has_key(NETWORK):
-		error =  "The option -n/--net and -b/--basename are mutually exclusive"
-	 	raise PxeConfig, error	
-
-	if binfo.has_key(BASENAME):
-		if binfo[RACK] and binfo[NODE]:
-			create_links = rack_2_net
-		else:
-			set_padding(binfo)
-			create_links =  base_2_host
-
-	elif binfo.has_key(NETWORK):
-		binfo[START] = check_number(binfo[START], True)
-		binfo[END] = check_number(binfo[END], True)
-		create_links = manage_links
-
-	else:
-		error = 'You have to specifiy -b,--basename or -n,--net'
-		raise PxeConfig, error
-
-	if DEBUG:
-		print binfo
-
-	create_links(binfo)
-
-def set_padding(binfo):
-	"""
-	binfo          : boot info
-	network_number : must we check if  start,end values are 
-	                 valid network numbers
-	return:
-		- if equal_width is requested then the length will be set to end value
-		- if start value length > 1 and start with a zero (0), width is set to
-		  the end value
-		- if end value starts with a zero (0), width will be set to the end 
-		  value
-	"""
-	start_str = binfo[START]
-	end_str = binfo[END]
-
-	start = check_number(start_str, False)
-	end = check_number(end_str, False)
-
-	if binfo[EQUALWIDTH][0] == True:
-		binfo[EQUALWIDTH][1] = len(end_str) 
-
-	elif len(start_str) > 1 and start_str[0] == '0':
-		binfo[EQUALWIDTH] = [ True, len(end_str) ]
-
-	elif end_str[0] == '0':
-		binfo[EQUALWIDTH] = [ True, len(end_str) ]
-
-	binfo[START] = start
-	binfo[END] = end
-
-
-def parse_number_range(binfo, arg):
-	"""
-	Parse if arg is of format <digit-digit>, if it starts 
-	with a zero (0) then set EQUALWIDTH
-	"""
-	str = 'parse_hostrange %s' %(arg)
-	verbose(str)
-
-	l = arg.split('-')
-	if len(l) < 2:
-		error =  'hostrange syntax not valid: %s (number-number)' %(arg)
-		raise PxeConfig, error
-
-	binfo[START] = l[0]
-	binfo[END] = l[1]
-
-def parse_string_range(binfo, arg, id):
-	"""
-	Parse if arg is of format <(alpha)(digit)>-(alpha)(digit)>
-	if digit starts with a zero (0) then set EQUALWIDTH
-	"""
-	str = 'parse_string_range: %s %s' %(arg, id)
-	verbose(str)
-
-	l = arg.split('-')
-	if len(l) < 2:
-		error =  '%s : range syntax not valid,eg <string>-<string>)' %(arg)
-		raise PxeConfig, error
-	
-	binfo[id] = dict()
-	binfo[id][EQUALWIDTH] = [False, 0]
-
-	i = 0
-	for item in l: 
-		result = id_re.match(item)
-
-		if result:
-			basename = result.group('basename')
-			number = result.group('number')
-
-			if DEBUG:
-				print 'basename = %s, number = %s' %(basename, number)
-	
-			if i == 0:
-				binfo[id][BASENAME] = basename
-				binfo[id][START] = number
-				i += 1
-			else:
-				binfo[id][END] = number
-
-		else:
-			error = '%s : string syntax is not valid, eg: <alpa>*<digit>+' %(item) 
-			raise PxeConfig, error
-
-        set_padding(binfo[id])	
 
 def add_options(p):
@@ -468,4 +225,10 @@
 	)
 
+	p.add_option('-n', '--dry-run',
+		action = 'store_true',
+		dest   = 'DRY_RUN',
+		help   = 'Do not execute any command'
+	)
+
 	p.add_option('-f', '--filename',
 		action = 'store',
@@ -480,121 +243,49 @@
 	)
 
+	p.add_option('-v', '--verbose',
+		action = 'store_true',
+		dest   = 'VERBOSE',
+		help   = 'Make the program more verbose'
+	)
+
 	p.add_option('-V', '--version',
 		action = 'store_true',
 		dest   = 'VERSION',
-		help   = 'Removes the PXE filename for a host(s)'
-	)
-
-
-def new_parser(argv): 
-	"""
-	Make use of sara advance parser module
-	"""
-	parser = AdvancedParser.AdvancedParser(version='Version: 3.0.0', usage=__doc__)
+		help   = 'Print the program version number and exit'
+	)
+
+def parser(argv): 
+	"""
+	Make use of sara advance parser module. It can handle regex in hostnames
+	"""
+	parser = AdvancedParser.AdvancedParser(usage=__doc__)
 	add_options(parser)
 
-	options, args = parser.parse_args()
-	return (options, args)
-
-	
-def parse_args(argv, binfo):
-	"""
-	This function parses the command line options and returns the rest as
-	an list of hostnames:
-	argv     : a list of command line options.
-	binfo    : returning a dict with the netinfo. if used non-interactively
-	hostnames: the rest of the command lines options that are not-parseble.
-	"""
-	try:
-		opts, args = getopt.gnu_getopt(argv[1:], SHORTOPT_LIST, LONGOPT_LIST)
-	except getopt.error, detail:
-		print __doc__
-		print detail
-		sys.exit(1) 
-	
-	global DEBUG
-	global VERBOSE
-	global DRY_RUN
-
-	# if nothing is specified then print usage and exit
-	#
-	if not opts and not args:
-		print __doc__
-		sys.exit(1)
-
-	# init vars
-	#
-	hostrange = node = rack = None
-
-	# Check given options
-	#
-	for opt,value in opts:
-			
-		if opt in ['-b', '--basename']:
-			binfo[BASENAME] = value
-			
-		elif opt in ['--debug']:
-			DEBUG = True 
-
-		elif opt in ['--dry-run']:
-			DRY_RUN = True 
-			
-		elif opt in ['-e', '--end']: 
-			binfo[END] = value 
-			
-		elif opt in ['-f', '--file']:
-			binfo[FILENAME] = value 
-			
-		elif opt in ['-h', '--help']:
-			print __doc__
-			sys.exit(0) 
-
-		elif opt in ['-i', '--interactive']:
-			interactive(binfo)
-			sys.exit(0) 
-
-		elif opt in ['-n', '--net']:
-			binfo[NETWORK] = net_2_hex(value) 
-			
-		elif opt in ['-r', '--remove']:
-			binfo[REMOVE] = 1 
-			
-		elif opt in ['-s', '--start']:
-			binfo[START] = value 
-
-		elif opt in ['-w', '--equal-width']:
-			binfo[EQUALWIDTH] = [True, 0]
-
-		elif opt in ['-v', '--verbose']:
-			VERBOSE = True
-
-		elif opt in ['-H', '--host-range']:
-			hostrange = value
-
-		elif opt in ['-N', '--node']:
-			node = value
-
-		elif opt in ['-R', '--rack']:
-			rack = value
-			
-		elif opt in ['-V', '--version']:
-			print VERSION
-			sys.exit(0) 
-
-	if node and rack:
-		 	parse_string_range(binfo, node, NODE)
-		 	parse_string_range(binfo, rack, RACK)
-	elif hostrange:
-		 	parse_number_range(binfo, hostrange)
-		
-
-	check_args(binfo, args)
-
-def host_2_hex(hosts, binfo):
+	options, args = parser.parse_args() 
+	
+	if options.VERSION:
+		print VERSION
+		sys.exit(0) 
+
+	if options.filename:
+		if not os.path.isfile(os.path.join(PXE_CONF_DIR, options.filename)):
+				error =  '%s: Filename does not exists' %(options.filename)
+				print error
+				sys.exit(1)
+	else:
+		options.filename = select_pxe_configfile() 
+
+	if options.DEBUG:
+		print args, options
+
+	hosts_2_hex(args, options)
+
+def hosts_2_hex(hosts, options):
 	"""
 	Convert hostname(s) to a net address that can be handled by manage_links function
 	"""
-	str = 'host_2_hex: %s' %hosts
-	verbose(str)
+	if options.VERBOSE:
+		str = 'host_2_hex: %s' %hosts
+		print str
 
 	for host in hosts:
@@ -603,87 +294,22 @@
 		except socket.error,detail:
 			error =  '%s not an valid hostname: %s' %(host,detail)
-			raise PxeConfig, error
-			
+			print error
+			sys.exit(1)
+
 		net = string.splitfields(addr, '.')
 		cnet = string.joinfields(net[0:3], '.')
 
-		binfo[NETWORK] = net_2_hex(cnet)
+		binfo = dict()
+		binfo[NETWORK] = net_2_hex(cnet, options)
 		binfo[START] = int(net[3])
 		binfo[END] =  int(net[3])
-		manage_links(binfo)
-
-def base_2_host(binfo):
-	"""
-	Construct hostname(s) from the supplied basename and start and end numbers
-	"""
-	str = 'base_2_host'
-	verbose(str)
-
-	start = binfo[START]
-	end = binfo[END]
-
-	if start > end:
-		error = '%d >= %d : start value is greater then end value' %(start, end)
-		raise PxeConfig, error
-
-	hostnames = list()
-	for i in xrange(start, end + 1):
-		if binfo[EQUALWIDTH][0] == True:
-			hostname = '%s%0*d' %(binfo[BASENAME],  binfo[EQUALWIDTH][1], i)
-		else:
-			hostname = '%s%d' %(binfo[BASENAME], i)
-
-		if DEBUG:
-			print 'host = %s, Basename = %s, number = %d' %(hostname, binfo[BASENAME], i)
-		hostnames.append(hostname)
-
-	host_2_hex(hostnames,binfo)
-
-def rack_2_net(binfo):
-	"""
-	"""
-	str = 'rack_2_net'
-	verbose(str)
-	basename = binfo[BASENAME]
-	start = binfo[RACK][START]
-	end = binfo[RACK][END]
-
-	if start > end:
-		error = '%d >= %d : start value is greater then end value' %(start, end)
-		raise PxeConfig, error
-
-	for i in xrange(start, end + 1):
-		if binfo[RACK][EQUALWIDTH][0] == True:
-			new_base = '%s%s%0*d' %(basename,  binfo[RACK][BASENAME], binfo[RACK][EQUALWIDTH][1], i)
-		else:
-			new_base = '%s%s%d' %(basename, binfo[RACK][BASENAME], i)
-
-		# Make copy and file in the appropiate values for creating/removing links
-		#
-		new_bootinfo = binfo[NODE].copy()
-		new_bootinfo[BASENAME] = '%s%s' %(new_base, binfo[NODE][BASENAME])
-		new_bootinfo[FILENAME] = binfo[FILENAME]
-		new_bootinfo[REMOVE] = binfo[REMOVE]
-
-		if DEBUG:
-			print 'rack ', new_bootinfo
-
-		base_2_host(new_bootinfo)
-
-
-def new_main():
+		manage_links(binfo, options)
+		
 
 def main():
 	# A dictionary holding the boot info
 	#
-	global DEBUG 
 	global PXE_CONF_DIR
 	
-	bootinfo = {}
-	bootinfo[NODE] = None
-	bootinfo[RACK] = None
-	bootinfo[REMOVE] = False 
-	bootinfo[EQUALWIDTH] = [ False, 0 ]
-
 	configfile = '@pxeconfig_conf@'
 	settings = ReadConfig(configfile) 
@@ -691,6 +317,4 @@
 	try:
 		PXE_CONF_DIR = settings['pxe_config_dir']
-		if not DEBUG:
-			DEBUG = int(settings['debug'])
 
 	except KeyError:
@@ -702,10 +326,9 @@
 		raise PxeConfig, error
 
-	parse_args(sys.argv, bootinfo) 
+	parser(sys.argv)
 	
 if __name__ == '__main__':
 	try:
-		new_parser(sys.argv)
-		new_main()
+		main()
 	except PxeConfig, detail:
 		print detail
