source: trunk/pxeconfig.in @ 93

Last change on this file since 93 was 93, checked in by bas, 15 years ago

pxeconfig.in:

  • Fixed an error in getopt
  • Property keywords set to Id
  • Property svn:keywords set to Id
File size: 8.5 KB
Line 
1#!@PYTHON@
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#
8# SVN info
9#  $Id: pxeconfig.in 93 2007-03-29 10:04:56Z bas $
10#
11# Copyright (C) 2002
12#
13# This file is part of the pxeconfig utils
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.
19#
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.
24#
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#
29"""
30Usage: pxeconfig
31
32 [-f|--file <filename>] [hostname(s)]
33 [-i|--interactive]
34 [-n|--net <C-net> -s|--start <number> -e|--end <number> -f|--file <filename>]
35 [-r|--remove]
36 [-V|--version]
37
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:
43  1) Network address (Class C-network address only)
44  2) Starting number
45  3) Ending number
46  4) Which PXE config file to use
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
64import getopt
65import socket
66import ConfigParser
67
68# DEBUG
69#
70DEBUG=0
71
72# Constants
73#
74PXE_CONF_DIR='/tftpboot/pxelinux.cfg'
75NETWORK='network'
76FILENAME='filename'
77START='start'
78END='end'
79VERSION='0.6.2'
80REMOVE='remove'
81INTERACTIVE='interactive'
82
83SHORTOPT_LIST='hVn:s:e:f:ri'
84LONGOPT_LIST=['help', 'version', 'net=', 'start=', 'end=', 'file=', 'remove', 'debug' , 'interactive' ]
85
86def ReadConfig(file):
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
94        config = ConfigParser.RawConfigParser()
95        try:
96                config.read(file)
97        except ConfigParser.MissingSectionHeaderError,detail:
98                print detail
99                sys.exit(1)
100
101        # Not yet uses
102        #
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)
108
109        stanza = config.defaults()
110        return stanza
111
112def select_pxe_configfile():
113  """
114  Let user choose which pxeconfig file to use.
115  """
116
117  os.chdir(PXE_CONF_DIR)
118
119  # Try to determine to which file the default symlink points, and
120  # if it exists, remove it from the list.
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)
135 
136
137  print 'Which pxe config file must we use: ?'
138  i = 1   
139  for file in files:
140    print "%d : %s" %(i,file)
141    i = i +1
142
143  while 1:
144    index = raw_input('Select a number: ')
145    try:
146      index = int(index)
147    except ValueError:
148      index = len(files) + 1
149
150    # Is the user smart enough to select
151    # the right value??
152    #
153    if 0 < index <= len(files): break
154
155  return files[index-1]
156
157def manage_links(dict):
158  """
159  Create the links in the PXE_CONF_DIR,
160    list : A list containing: network hex address, pxe config file,
161           start number, end number
162  """
163  os.chdir(PXE_CONF_DIR)
164  naddr = dict[NETWORK]
165  pxe_filename = dict[FILENAME]
166  for i in range(dict[START], dict[END]+1):
167    haddr = '%s%02X' %(naddr, i)
168
169    if dict[REMOVE]:
170       if DEBUG:
171          print 'removing %s/%s' %(PXE_CONF_DIR, haddr)
172       if os.path.exists(haddr):
173          os.unlink(haddr)
174    else:
175       if DEBUG:
176          print 'linking %s to %s' %(haddr, pxe_filename)
177       if os.path.exists(haddr):
178          os.unlink(haddr)
179       os.symlink(pxe_filename, haddr)
180
181def convert_network(net, prefix=''):
182  """
183  This function checks if the give network is a Class C-network and will
184  convert the network address to a hex address if true.
185  """
186  d = string.split(net, '.')
187
188  if len(d) != 3:
189    if prefix:
190      net = prefix + ' : ' + net
191     
192    print '%s is not a valid  C-class network address!!!' %net
193    sys.exit(1)
194
195
196  # Display right message for interactive/commandline
197  if prefix:
198    prefix = prefix + ' ' + net
199  else:
200    prefix = net
201
202  # Check if we have valid network values
203  r = ''
204  for i in d:
205    i = check_number(i, prefix)
206    r = '%s%02X' %(r,i)
207
208  if DEBUG:
209    print r
210
211  return r
212
213def check_number(number, prefix=''):
214  """
215  This functions checks if the input is between 0 < number < 255:
216     number : is a string
217     prefix ; a string that must be displayed if an error occurs
218  """
219  try:
220    n = int(number)
221  except ValueError, detail:
222    print prefix,detail
223    sys.exit(1)
224
225  if 0 <= n <= 255:
226    return n
227  else:
228
229    if prefix:
230      number = prefix +' : ' + number
231
232    print '%s is not a valid number, must be between 0 and 255' %number
233    sys.exit(1)
234
235def interactive(binfo):
236
237  print __doc__
238
239  network = raw_input('Give network address (xxx.xxx.xxx): ')
240  naddr = convert_network(network)
241
242  start = raw_input('Starting number: ')
243  start = check_number(start)
244
245  end = raw_input('Ending number: ')
246  end = check_number(end)
247
248  pxe_filename = select_pxe_configfile()
249
250  binfo[NETWORK] = naddr
251  binfo[START] = int(start)
252  binfo[END] = int(end)
253  binfo[FILENAME] = pxe_filename
254
255  if DEBUG:
256    print network, binfo
257
258def check_cmd_line(binfo):
259  if len(binfo.keys()) != 5:
260    print __doc__
261    print 'Not enough arguments to create the links!!'
262    sys.exit(1)
263
264  # check_filename
265  #
266  if not os.path.isfile(os.path.join(PXE_CONF_DIR, binfo[FILENAME])):
267    print '%s: Filename does not exists' %binfo[FILENAME]
268    sys.exit(1)
269
270def check_args(argv, binfo):
271  """
272  This function parses the command line options and returns the rest as
273  an list of hostnames:
274    argv     : a list of command line options.
275    binfo    : returning a dict with the netinfo. if used non-interactively
276    hostnames: the rest of the command lines options that are not-parseble.
277  """
278  try:
279    opts, args = getopt.getopt(argv[1:], SHORTOPT_LIST, LONGOPT_LIST)
280  except getopt.error, detail:
281    print __doc__
282    print detail
283    sys.exit(1)
284
285  # Check given options
286  #
287  for opt,value in opts:
288    if opt in ['-i', '--interactive']:
289      binfo[INTERACTIVE] = 1
290
291    elif opt in ['-n', '--net']:
292      network = value
293      binfo[NETWORK] = convert_network(value, opt)
294
295    elif opt in ['-s', '--start']:
296      binfo[START] = check_number(value, opt)
297
298    elif opt in ['-e', '--end']:
299      binfo[END] = check_number(value, opt)
300
301    elif opt in ['-f', '--file']:
302      binfo[FILENAME] = value
303
304    elif opt in ['-V', '--version']:
305      print VERSION
306      sys.exit(0)
307
308    elif opt in ['-r', '--remove']:
309      binfo[REMOVE] = 1
310
311    elif opt in ['--debug']:
312      DEBUG = 1
313
314    elif opt in ['-h', '--help']:
315      print __doc__
316      sys.exit(0)
317
318  if args:
319        return args
320
321def hosts_links(hosts, binfo):
322        for host in hosts:
323                try:
324                        addr = socket.gethostbyname(host)
325                except socket.error,detail:
326                        print '%s not an valid hostname: %s' %(host,detail)
327                        sys.exit(1)
328                       
329                net = string.splitfields(addr, '.')
330                cnet = string.joinfields(net[0:3], '.')
331
332                binfo[NETWORK] = convert_network(cnet)
333                binfo[START] = int(net[3])
334                binfo[END] =  int(net[3])
335                manage_links(binfo)
336
337
338def main():
339  # A dictionary holding the boot info
340  #
341  global PXE_CONF_DIR
342  global DEBUG
343
344  bootinfo = {}
345  bootinfo[REMOVE] = 0
346
347  configfile = '@pxeconfig_conf@'
348  settings = ReadConfig(configfile)
349
350  try:
351        PXE_CONF_DIR = settings['pxe_config_dir']
352        DEBUG = settings['debug']
353  except KeyError:
354        pass
355
356  hostnames = check_args(sys.argv, bootinfo)
357
358  if bootinfo[REMOVE]:
359     bootinfo[FILENAME] = 'remove_boot_file'
360
361
362  # If we supplied args then make links for the supplied hosts
363  # else make links for class C-networks
364  #
365  if hostnames:
366        if not bootinfo.has_key(FILENAME):
367              bootinfo[FILENAME] = select_pxe_configfile()
368        hosts_links(hostnames,bootinfo)
369  else:
370        if bootinfo.has_key(INTERACTIVE):   
371                interactive(bootinfo)
372        else:
373                check_cmd_line(bootinfo)
374        manage_links(bootinfo)
375
376if __name__ == '__main__':
377  main()
Note: See TracBrowser for help on using the repository browser.