source: trunk/pxeconfig.in @ 108

Last change on this file since 108 was 108, checked in by bas, 16 years ago

pxeconfig.in, pxeconfigd.in:

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