source: trunk/pxeconfig.in @ 114

Last change on this file since 114 was 114, checked in by bas, 14 years ago

Fixed a bug in the -r/--remove option. Do not display
the pxe filename menu

  • Property keywords set to Id
  • Property svn:executable set to *
  • Property svn:keywords set to Id
File size: 10.0 KB
Line 
1#!@PYTHON@
2#
3# set ts=4, sw=4
4#
5# Author: Bas van der Vlies <basv@sara.nl>
6# Date  : 16 February 2002
7#
8# Tester: Walter de Jong <walter@sara.nl>
9#
10# SVN info
11#  $Id: pxeconfig.in 114 2007-10-25 12:58:15Z bas $
12#
13# Copyright (C) 2002
14#
15# This file is part of the pxeconfig utils
16#
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.
21#
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.
26#
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#
31"""
32Usage: pxeconfig
33
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
39
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:
45  1) Network address (Class C-network address only)
46  2) Starting number
47  3) Ending number
48  4) Which PXE config file to use
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
66import getopt
67import socket
68import ConfigParser
69
70# DEBUG
71#
72DEBUG=0
73
74# Constants
75#
76PXE_CONF_DIR='/tftpboot/pxelinux.cfg'
77NETWORK='network'
78BASENAME='basename'
79FILENAME='filename'
80START='start'
81END='end'
82REMOVE='remove'
83INTERACTIVE='interactive'
84VERSION='1.0.0'
85
86SHORTOPT_LIST='be:f:hin:s:rV'
87LONGOPT_LIST=['basename=', 'debug', 'end=', 'file=', 'help', 'interactive', 'net=', 'start=', 'remove', 'version' ]
88
89def ReadConfig(file):
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
97        config = ConfigParser.RawConfigParser()
98        try:
99                config.read(file)
100        except ConfigParser.MissingSectionHeaderError,detail:
101                print detail
102                sys.exit(1)
103
104        # Not yet uses
105        #
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)
111
112        stanza = config.defaults()
113        return stanza
114
115def select_pxe_configfile():
116  """
117  Let user choose which pxeconfig file to use.
118  """
119
120  os.chdir(PXE_CONF_DIR)
121
122  # Try to determine to which file the default symlink points, and
123  # if it exists, remove it from the list.
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)
138
139  # sort the files
140  #
141  files.sort()
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:
150    index = raw_input('Select a number: ')
151    try:
152      index = int(index)
153    except ValueError:
154      index = len(files) + 1
155
156    # Is the user smart enough to select
157    # the right value??
158    #
159    if 0 < index <= len(files): break
160
161  return files[index-1]
162
163def manage_links(dict):
164  """
165  Create the links in the PXE_CONF_DIR,
166    list : A list containing: network hex address, pxe config file,
167           start number, end number
168  """
169  os.chdir(PXE_CONF_DIR)
170  naddr = dict[NETWORK]
171  pxe_filename = dict[FILENAME]
172  for i in range(dict[START], dict[END]+1):
173    haddr = '%s%02X' %(naddr, i)
174
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)
186
187def convert_network(net, prefix=''):
188  """
189  This function checks if the give network is a Class C-network and will
190  convert the network address to a hex address if true.
191  """
192  d = string.split(net, '.')
193
194  if len(d) != 3:
195    if prefix:
196      net = prefix + ' : ' + net
197     
198    print '%s is not a valid  C-class network address!!!' %net
199    sys.exit(1)
200
201
202  # Display right message for interactive/commandline
203  if prefix:
204    prefix = prefix + ' ' + net
205  else:
206    prefix = net
207
208  # Check if we have valid network values
209  r = ''
210  for i in d:
211    i = check_number(i, prefix)
212    r = '%s%02X' %(r,i)
213
214  if DEBUG:
215    print r
216
217  return r
218
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)
230
231        if not network:
232                return n
233
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)
244
245def interactive(binfo):
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
266
267        manage_links(binfo)
268
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                if binfo[REMOVE] :
281                        binfo[FILENAME] = 'Does not matter'
282                else:
283                        binfo[FILENAME] = select_pxe_configfile()
284
285        if hostnames:
286                host_2_net(hostnames, binfo)
287                sys.exit(0)
288               
289        if binfo.has_key(BASENAME) and binfo.has_key(NETWORK):
290                print __doc__
291                print "The option -n/--net and -b/--basename are mutually exclusive"
292                sys.exit(1)
293
294        if binfo.has_key(BASENAME):
295                network_number = False
296                create_links =  base_2_net
297
298        elif binfo.has_key(NETWORK):
299                network_number = True
300                create_links = manage_links
301        else:
302                print __doc__
303                sys.exit(1)
304
305        try:
306                binfo[START] = check_number(binfo[START], network_number, '-s/--start')
307        except KeyError, detail:
308                print __doc__
309                print '-s/--start is missing on the command line' %(detail)
310                sys.exit(1)
311
312        try:
313                binfo[END] = check_number(binfo[END], network_number, '-e/--end')
314        except KeyError, detail:
315                print __doc__
316                print '-e/--end is missing on the command line' %(detail)
317                sys.exit(1)
318
319        if DEBUG:
320                print binfo
321
322        create_links(binfo)
323
324
325def check_args(argv, binfo):
326        """
327        This function parses the command line options and returns the rest as
328        an list of hostnames:
329        argv     : a list of command line options.
330        binfo    : returning a dict with the netinfo. if used non-interactively
331        hostnames: the rest of the command lines options that are not-parseble.
332        """
333        try:
334                opts, args = getopt.gnu_getopt(argv[1:], SHORTOPT_LIST, LONGOPT_LIST)
335        except getopt.error, detail:
336                print __doc__
337                print detail
338                sys.exit(1)
339       
340        global DEBUG
341
342        # if nothing is specified then print usage and exit
343        #
344        if not opts and not args:
345                print __doc__
346                sys.exit(1)
347
348        # Check given options
349        #
350        for opt,value in opts:
351                       
352                if opt in ['-b', '--basename']:
353                        binfo[BASENAME] = value
354                       
355                elif opt in ['--debug']:
356                        DEBUG = 1
357                       
358                elif opt in ['-e', '--end']:
359                        binfo[END] = value
360                       
361                elif opt in ['-f', '--file']:
362                        binfo[FILENAME] = value
363                       
364                elif opt in ['-h', '--help']:
365                        print __doc__
366                        sys.exit(0)
367
368                elif opt in ['-i', '--interactive']:
369                        interactive(binfo)
370                        sys.exit(0)
371
372                elif opt in ['-n', '--net']:
373                        network = value
374                        binfo[NETWORK] = convert_network(value, opt)
375                       
376                elif opt in ['-r', '--remove']:
377                        binfo[REMOVE] = 1
378                       
379                elif opt in ['-s', '--start']:
380                        binfo[START] = value
381
382                elif opt in ['-V', '--version']:
383                        print VERSION
384                        sys.exit(0)
385
386        check_command_line(binfo, args)
387
388def host_2_net(hosts, binfo):
389        """
390        Convert hostsname to a net address that can be handled by manage_links function
391        """
392        for host in hosts:
393                try:
394                        addr = socket.gethostbyname(host)
395                except socket.error,detail:
396                        print '%s not an valid hostname: %s' %(host,detail)
397                        sys.exit(1)
398                       
399                net = string.splitfields(addr, '.')
400                cnet = string.joinfields(net[0:3], '.')
401
402                binfo[NETWORK] = convert_network(cnet)
403                binfo[START] = int(net[3])
404                binfo[END] =  int(net[3])
405                manage_links(binfo)
406
407def base_2_net(binfo):
408        """
409        Construct hostname(s) from the supplied basename and start and end numbers
410        """
411        if binfo[START] >= binfo[END]:
412                print __doc__
413                print "Supplied wrong values for start (%d) and end (%d)" %(binfo[START], binfo[END])
414                sys.exit(1)
415
416        hostnames = list()
417        for i in xrange(binfo[START], binfo[END] + 1):
418                hostname = '%s%d' %(binfo[BASENAME], i)
419                if DEBUG:
420                        print 'host = %s, Basename = %s, number = %d' %(hostname, binfo[BASENAME], i)
421                hostnames.append(hostname)
422
423        host_2_net(hostnames,binfo)
424               
425def main():
426        # A dictionary holding the boot info
427        #
428        global DEBUG
429       
430        bootinfo = {}
431        bootinfo[REMOVE] = 0
432       
433        configfile = '@pxeconfig_conf@'
434        settings = ReadConfig(configfile)
435       
436        try:
437                PXE_CONF_DIR = settings['pxe_config_dir']
438                if not DEBUG:
439                        DEBUG = int(settings['debug'])
440        except KeyError:
441                pass
442
443        check_args(sys.argv, bootinfo)
444       
445if __name__ == '__main__':
446        main()
Note: See TracBrowser for help on using the repository browser.