source: trunk/pxeconfig.in @ 99

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

pxeconfig.in:

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