source: trunk/pxeconfig.in @ 104

Last change on this file since 104 was 104, checked in by bas, 17 years ago

pxeconfig.in:

  • Added -b/--basename to getopt
  • Improved code layout for some function, ts=4
  • Property keywords set to Id
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 9.5 KB
RevLine 
[78]1#!@PYTHON@
[2]2#
[104]3# set ts=4, sw=4
4#
[2]5# Author: Bas van der Vlies <basv@sara.nl>
6# Date  : 16 February 2002
7#
8# Tester: Walter de Jong <walter@sara.nl>
9#
[29]10# SVN info
11#  $Id: pxeconfig.in 104 2007-09-21 15:41:50Z bas $
[2]12#
[14]13# Copyright (C) 2002
[6]14#
[14]15# This file is part of the pxeconfig utils
[6]16#
[14]17# This program is free software; you can redistribute it and/or modify it
18# under the terms of the GNU General Public License as published by the
19# Free Software Foundation; either version 2, or (at your option) any
20# later version.
[6]21#
[14]22# This program is distributed in the hope that it will be useful,
23# but WITHOUT ANY WARRANTY; without even the implied warranty of
24# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25# GNU General Public License for more details.
[6]26#
[14]27# You should have received a copy of the GNU General Public License
28# along with this program; if not, write to the Free Software
29# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
30#
[2]31"""
[19]32Usage: pxeconfig
[49]33
[53]34 [-f|--file <filename>] [hostname(s)]
[58]35 [-i|--interactive]
[53]36 [-n|--net <C-net> -s|--start <number> -e|--end <number> -f|--file <filename>]
[104]37 [-b|--basename <string> s|--start <number> -e|--end <number> -f|--file <filename>]
[53]38 [-r|--remove]
[24]39 [-V|--version]
[10]40
[19]41With this program you can configure which PXE configuration file a node
42will use when it boots. The program can be started interactivly or via
43command line options.
44
45When this program is started interactive it will ask the following questions:
[2]46  1) Network address (Class C-network address only)
[9]47  2) Starting number
[2]48  3) Ending number
[9]49  4) Which PXE config file to use
[2]50
51For example, if the answers are:
52  1) 10.168.44
53  2) 2
54  3) 4
55  4) default.node_install
56
57Then the result is that is create symbolic links in /tftpboot/pxelinux.cfg:
58  0AA82C02 ----> default.node_install
59  0AA82C03 ----> default.node_install
60  0AA82C04 ----> default.node_install
61"""
62
63import string
64import sys
65import os
66import glob
[3]67import getopt
[47]68import socket
[74]69import ConfigParser
[2]70
71# DEBUG
72#
[104]73DEBUG=1
[2]74
75# Constants
76#
[4]77PXE_CONF_DIR='/tftpboot/pxelinux.cfg'
[19]78NETWORK='network'
[104]79BASENAME='basename'
[19]80FILENAME='filename'
81START='start'
82END='end'
[53]83REMOVE='remove'
[55]84INTERACTIVE='interactive'
[104]85VERSION='0.8.0'
[2]86
[104]87SHORTOPT_LIST='be:f:hin:s:rV'
88LONGOPT_LIST=['basename=', 'debug', 'end=', 'file=', 'help', 'interactive', 'net=', 'start=', 'remove', 'version' ]
[19]89
[75]90def ReadConfig(file):
[74]91        """
92        Parse the config file
93        """
94        if not os.path.isfile(file):
95                print 'File %s does not exist' %file
96                sys.exit(1)
97
[75]98        config = ConfigParser.RawConfigParser()
[74]99        try:
100                config.read(file)
101        except ConfigParser.MissingSectionHeaderError,detail:
102                print detail
103                sys.exit(1)
104
[75]105        # Not yet uses
[74]106        #
[75]107        #projects = {}
108        #for section in config.sections():
109        #       projects[section] = {}
110        #       for option in  config.options(section):
111        #       projects[section][option] = config.get(section, option)
[74]112
[75]113        stanza = config.defaults()
114        return stanza
[74]115
[19]116def select_pxe_configfile():
[2]117  """
[9]118  Let user choose which pxeconfig file to use.
[2]119  """
120
[4]121  os.chdir(PXE_CONF_DIR)
[2]122
[9]123  # Try to determine to which file the default symlink points, and
124  # if it exists, remove it from the list.
[2]125  #
126  try:
127    default_file = os.readlink('default')
128  except OSError:
129    default_file = None
130    pass
131
132  files = glob.glob('default.*')
133  if not files:
134    print 'There are no pxe config files starting with: default.'
135    sys.exit(1)
136
137  if default_file:
138    files.remove(default_file)
[98]139
140  # sort the files
141  #
142  files.sort()
[2]143 
144  print 'Which pxe config file must we use: ?'
145  i = 1   
146  for file in files:
147    print "%d : %s" %(i,file)
148    i = i +1
149
150  while 1:
[16]151    index = raw_input('Select a number: ')
[2]152    try:
153      index = int(index)
154    except ValueError:
155      index = len(files) + 1
156
157    # Is the user smart enough to select
[9]158    # the right value??
[2]159    #
160    if 0 < index <= len(files): break
161
162  return files[index-1]
163
[53]164def manage_links(dict):
[2]165  """
[4]166  Create the links in the PXE_CONF_DIR,
[9]167    list : A list containing: network hex address, pxe config file,
[2]168           start number, end number
169  """
[4]170  os.chdir(PXE_CONF_DIR)
[19]171  naddr = dict[NETWORK]
172  pxe_filename = dict[FILENAME]
173  for i in range(dict[START], dict[END]+1):
[2]174    haddr = '%s%02X' %(naddr, i)
[15]175
[53]176    if dict[REMOVE]:
177       if DEBUG:
178          print 'removing %s/%s' %(PXE_CONF_DIR, haddr)
179       if os.path.exists(haddr):
180          os.unlink(haddr)
181    else:
182       if DEBUG:
183          print 'linking %s to %s' %(haddr, pxe_filename)
184       if os.path.exists(haddr):
185          os.unlink(haddr)
186       os.symlink(pxe_filename, haddr)
[15]187
[47]188def convert_network(net, prefix=''):
[2]189  """
[9]190  This function checks if the give network is a Class C-network and will
[2]191  convert the network address to a hex address if true.
192  """
193  d = string.split(net, '.')
194
195  if len(d) != 3:
[19]196    if prefix:
197      net = prefix + ' : ' + net
198     
199    print '%s is not a valid  C-class network address!!!' %net
[2]200    sys.exit(1)
201
[19]202
203  # Display right message for interactive/commandline
204  if prefix:
205    prefix = prefix + ' ' + net
206  else:
207    prefix = net
208
[2]209  # Check if we have valid network values
210  r = ''
211  for i in d:
[19]212    i = check_number(i, prefix)
[2]213    r = '%s%02X' %(r,i)
214
215  if DEBUG:
216    print r
217
218  return r
219
[19]220def check_number(number, prefix=''):
[2]221  """
222  This functions checks if the input is between 0 < number < 255:
223     number : is a string
[19]224     prefix ; a string that must be displayed if an error occurs
[2]225  """
226  try:
227    n = int(number)
228  except ValueError, detail:
[19]229    print prefix,detail
[2]230    sys.exit(1)
231
232  if 0 <= n <= 255:
233    return n
234  else:
[19]235
236    if prefix:
237      number = prefix +' : ' + number
238
[2]239    print '%s is not a valid number, must be between 0 and 255' %number
240    sys.exit(1)
241
[19]242def interactive(binfo):
[2]243
244  print __doc__
245
[9]246  network = raw_input('Give network address (xxx.xxx.xxx): ')
[47]247  naddr = convert_network(network)
[2]248
249  start = raw_input('Starting number: ')
250  start = check_number(start)
251
252  end = raw_input('Ending number: ')
253  end = check_number(end)
254
[19]255  pxe_filename = select_pxe_configfile()
[2]256
[19]257  binfo[NETWORK] = naddr
258  binfo[START] = int(start)
259  binfo[END] = int(end)
260  binfo[FILENAME] = pxe_filename
[2]261
[19]262  if DEBUG:
263    print network, binfo
[2]264
[19]265def check_cmd_line(binfo):
[60]266  if len(binfo.keys()) != 5:
[19]267    print __doc__
[20]268    print 'Not enough arguments to create the links!!'
[19]269    sys.exit(1)
[6]270
[19]271  # check_filename
272  #
273  if not os.path.isfile(os.path.join(PXE_CONF_DIR, binfo[FILENAME])):
274    print '%s: Filename does not exists' %binfo[FILENAME]
275    sys.exit(1)
276
277def check_args(argv, binfo):
[104]278        """
279        This function parses the command line options and returns the rest as
280        an list of hostnames:
281        argv     : a list of command line options.
282        binfo    : returning a dict with the netinfo. if used non-interactively
283        hostnames: the rest of the command lines options that are not-parseble.
284        """
285        try:
286                opts, args = getopt.gnu_getopt(argv[1:], SHORTOPT_LIST, LONGOPT_LIST)
287        except getopt.error, detail:
288                print __doc__
289                print detail
290                sys.exit(1)
291       
292        global DEBUG
[3]293
[104]294        # Check given options
295        #
296        for opt,value in opts:
297                if opt in ['-i', '--interactive']:
298                        binfo[INTERACTIVE] = 1
299                       
300                elif opt in ['-b', '--basename']:
301                        binfo[BASENAME] = value
302                       
303                elif opt in ['--debug']:
304                        DEBUG = 1
305                       
306                elif opt in ['-e', '--end']:
307                        binfo[END] = check_number(value, opt)
308                       
309                elif opt in ['-f', '--file']:
310                        binfo[FILENAME] = value
311                       
312                elif opt in ['-h', '--help']:
313                        print __doc__
314                        sys.exit(0)
315                       
316                elif opt in ['-n', '--net']:
317                        network = value
318                        binfo[NETWORK] = convert_network(value, opt)
319                       
320                elif opt in ['-r', '--remove']:
321                        binfo[REMOVE] = 1
322                       
323                elif opt in ['-s', '--start']:
324                        binfo[START] = check_number(value, opt)
[55]325
[104]326                elif opt in ['-V', '--version']:
327                        print VERSION
328                        sys.exit(0)
329                       
330        if args:
331                return args
[4]332
[104]333def host_2_net(hosts, binfo):
334        """
335        Convert hostsname to a net address that can be handled by manage_links function
336        """
[47]337        for host in hosts:
338                try:
339                        addr = socket.gethostbyname(host)
340                except socket.error,detail:
341                        print '%s not an valid hostname: %s' %(host,detail)
342                        sys.exit(1)
343                       
344                net = string.splitfields(addr, '.')
345                cnet = string.joinfields(net[0:3], '.')
346
347                binfo[NETWORK] = convert_network(cnet)
348                binfo[START] = int(net[3])
349                binfo[END] =  int(net[3])
[53]350                manage_links(binfo)
[47]351
[104]352def base_2_net(binfo):
353        """
354        Construct hostname(s) from the supplied basename and start and end numbers
355        """
356        try:
357                if binfo[START] >= binfo[END]:
358                        print __doc__
359                        print "Supplied wrong values for start and end"
360                        sys.exit(1)
361        except KeyError, detail:
362                print __doc__
363                print '%s is missing on the command line' %(detail)
364                sys.exit(1)
[47]365
[104]366        hostnames = list()
367        for i in xrange(binfo[START], binfo[END] + 1):
368                hostname = '%s%d' %(binfo[BASENAME], i)
369                if DEBUG:
370                        print 'host =%s, Basename = %s, number = %d' %(hostname, binfo[BASENAME], i)
371                hostnames.append(hostname)
372        host_2_net(hostnames,binfo)
373               
[2]374def main():
[104]375        # A dictionary holding the boot info
376        #
377        global DEBUG
378       
379        bootinfo = {}
380        bootinfo[REMOVE] = 0
381       
382        configfile = '@pxeconfig_conf@'
383        settings = ReadConfig(configfile)
384       
385        try:
386                PXE_CONF_DIR = settings['pxe_config_dir']
387                DEBUG = int(settings['debug'])
388        except KeyError:
389                pass
390       
391        hostnames = check_args(sys.argv, bootinfo)
392        print hostnames
393       
394        if bootinfo[REMOVE]:
395                bootinfo[FILENAME] = 'remove_boot_file'
396               
397        # If we have args then make links for the supplied hosts
398        # else make links for class C-networks
399        #
400        if hostnames:
401                if not bootinfo.has_key(FILENAME):
402                        bootinfo[FILENAME] = select_pxe_configfile()
403                host_2_net(hostnames,bootinfo)
[75]404
[104]405        elif bootinfo.has_key(BASENAME):
406                if not bootinfo.has_key(FILENAME):
407                        bootinfo[FILENAME] = select_pxe_configfile()
408                base_2_net(bootinfo)
409               
410        else:
411                if bootinfo.has_key(INTERACTIVE):   
412                        interactive(bootinfo)
413                else:
414                        check_cmd_line(bootinfo)
415                manage_links(bootinfo)
[2]416
417if __name__ == '__main__':
[104]418        main()
Note: See TracBrowser for help on using the repository browser.