source: trunk/pxeconfig/pxeconfig @ 60

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

pxeconfig, Changelog:

  • New pxeconfig version fixed a bug in no-interactive mode fixed and reported by: Charles Galpin <charles@…>
  • Property keywords set to Id
  • Property svn:keywords set to Id
File size: 8.0 KB
Line 
1#!/usr/bin/env 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 60 2005-10-31 11:50:24Z 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 [-d|--directory <pxe_config_dir>]
33 [-f|--file <filename>] [hostname(s)]
34 [-i|--interactive]
35 [-n|--net <C-net> -s|--start <number> -e|--end <number> -f|--file <filename>]
36 [-r|--remove]
37 [-V|--version]
38
39With this program you can configure which PXE configuration file a node
40will use when it boots. The program can be started interactivly or via
41command line options.
42
43When this program is started interactive it will ask the following questions:
44  1) Network address (Class C-network address only)
45  2) Starting number
46  3) Ending number
47  4) Which PXE config file to use
48
49For example, if the answers are:
50  1) 10.168.44
51  2) 2
52  3) 4
53  4) default.node_install
54
55Then the result is that is create symbolic links in /tftpboot/pxelinux.cfg:
56  0AA82C02 ----> default.node_install
57  0AA82C03 ----> default.node_install
58  0AA82C04 ----> default.node_install
59"""
60
61import string
62import sys
63import os
64import glob
65import getopt
66import socket
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='hVd:n:s:e:f:ri'
84LONGOPT_LIST=['help', 'version', 'directory=', 'net=', 'start=', 'end=', 'file=', 'remove', 'debug' , 'interactive' ]
85
86def select_pxe_configfile():
87  """
88  Let user choose which pxeconfig file to use.
89  """
90
91  os.chdir(PXE_CONF_DIR)
92
93  # Try to determine to which file the default symlink points, and
94  # if it exists, remove it from the list.
95  #
96  try:
97    default_file = os.readlink('default')
98  except OSError:
99    default_file = None
100    pass
101
102  files = glob.glob('default.*')
103  if not files:
104    print 'There are no pxe config files starting with: default.'
105    sys.exit(1)
106
107  if default_file:
108    files.remove(default_file)
109 
110
111  print 'Which pxe config file must we use: ?' 
112  i = 1   
113  for file in files:
114    print "%d : %s" %(i,file)
115    i = i +1
116
117  while 1:
118    index = raw_input('Select a number: ')
119    try:
120      index = int(index)
121    except ValueError:
122      index = len(files) + 1
123
124    # Is the user smart enough to select
125    # the right value??
126    #
127    if 0 < index <= len(files): break
128
129  return files[index-1]
130
131def manage_links(dict):
132  """
133  Create the links in the PXE_CONF_DIR,
134    list : A list containing: network hex address, pxe config file,
135           start number, end number
136  """
137  os.chdir(PXE_CONF_DIR)
138  naddr = dict[NETWORK]
139  pxe_filename = dict[FILENAME]
140  for i in range(dict[START], dict[END]+1):
141    haddr = '%s%02X' %(naddr, i)
142
143    if dict[REMOVE]:
144       if DEBUG:
145          print 'removing %s/%s' %(PXE_CONF_DIR, haddr)
146       if os.path.exists(haddr):
147          os.unlink(haddr)
148    else:
149       if DEBUG:
150          print 'linking %s to %s' %(haddr, pxe_filename)
151       if os.path.exists(haddr):
152          os.unlink(haddr)
153       os.symlink(pxe_filename, haddr)
154
155def convert_network(net, prefix=''):
156  """
157  This function checks if the give network is a Class C-network and will
158  convert the network address to a hex address if true.
159  """
160  d = string.split(net, '.')
161
162  if len(d) != 3:
163    if prefix:
164      net = prefix + ' : ' + net
165     
166    print '%s is not a valid  C-class network address!!!' %net
167    sys.exit(1)
168
169
170  # Display right message for interactive/commandline
171  if prefix:
172    prefix = prefix + ' ' + net
173  else:
174    prefix = net
175
176  # Check if we have valid network values
177  r = ''
178  for i in d:
179    i = check_number(i, prefix)
180    r = '%s%02X' %(r,i)
181
182  if DEBUG:
183    print r
184
185  return r
186
187def check_number(number, prefix=''):
188  """
189  This functions checks if the input is between 0 < number < 255:
190     number : is a string
191     prefix ; a string that must be displayed if an error occurs
192  """
193  try:
194    n = int(number)
195  except ValueError, detail:
196    print prefix,detail
197    sys.exit(1)
198
199  if 0 <= n <= 255:
200    return n
201  else:
202
203    if prefix:
204      number = prefix +' : ' + number
205
206    print '%s is not a valid number, must be between 0 and 255' %number
207    sys.exit(1)
208
209def interactive(binfo):
210
211  print __doc__
212
213  network = raw_input('Give network address (xxx.xxx.xxx): ') 
214  naddr = convert_network(network)
215
216  start = raw_input('Starting number: ') 
217  start = check_number(start)
218
219  end = raw_input('Ending number: ') 
220  end = check_number(end)
221
222  pxe_filename = select_pxe_configfile()
223
224  binfo[NETWORK] = naddr
225  binfo[START] = int(start)
226  binfo[END] = int(end)
227  binfo[FILENAME] = pxe_filename
228
229  if DEBUG:
230    print network, binfo
231
232def check_cmd_line(binfo):
233  if len(binfo.keys()) != 5:
234    print __doc__
235    print 'Not enough arguments to create the links!!'
236    sys.exit(1)
237
238  # check_filename
239  #
240  if not os.path.isfile(os.path.join(PXE_CONF_DIR, binfo[FILENAME])):
241    print '%s: Filename does not exists' %binfo[FILENAME]
242    sys.exit(1)
243
244def check_args(argv, binfo):
245  """
246  This function parses the command line options and returns the rest as
247  an list of hostnames:
248    argv     : a list of command line options.
249    binfo    : returning a dict with the netinfo. if used non-interactively
250    hostnames: the rest of the command lines options that are not-parseble.
251  """
252  global PXE_CONF_DIR
253  global DEBUG
254
255  try:
256    opts, args = getopt.getopt(argv[1:], SHORTOPT_LIST, LONGOPT_LIST)
257  except getopt.error, detail:
258    print __doc__
259    print detail
260    sys.exit(1)
261
262  # Check given options
263  #
264  for opt,value in opts:
265
266    if opt in ['-d', '--directory']:
267      if os.path.isdir(value):
268        PXE_CONF_DIR = value
269      else:
270        print 'Directory %s does not exists\n' %value
271        sys.exit(1)
272
273    elif opt in ['-i', '--interactive']:
274      binfo[INTERACTIVE] = 1
275
276    elif opt in ['-n', '--net']:
277      network = value
278      binfo[NETWORK] = convert_network(value, opt)
279
280    elif opt in ['-s', '--start']:
281      binfo[START] = check_number(value, opt)
282
283    elif opt in ['-e', '--end']:
284      binfo[END] = check_number(value, opt)
285
286    elif opt in ['-f', '--file']:
287      binfo[FILENAME] = value
288
289    elif opt in ['-V', '--version']:
290      print VERSION
291      sys.exit(0)
292
293    elif opt in ['-r', '--remove']:
294      binfo[REMOVE] = 1
295
296    elif opt in ['--debug']:
297      DEBUG = 1
298
299    elif opt in ['-h', '--help']:
300      print __doc__
301      sys.exit(0)
302
303  if args:
304        return args
305
306def hosts_links(hosts, binfo):
307        for host in hosts:
308                try:
309                        addr = socket.gethostbyname(host)
310                except socket.error,detail:
311                        print '%s not an valid hostname: %s' %(host,detail)
312                        sys.exit(1)
313                       
314                net = string.splitfields(addr, '.')
315                cnet = string.joinfields(net[0:3], '.')
316
317                binfo[NETWORK] = convert_network(cnet)
318                binfo[START] = int(net[3])
319                binfo[END] =  int(net[3])
320                manage_links(binfo)
321
322
323def main():
324  # A dictionary holding the boot info
325  #
326  bootinfo = {}
327  bootinfo[REMOVE] = 0
328
329  hostnames = check_args(sys.argv, bootinfo)
330
331  if bootinfo[REMOVE]:
332     bootinfo[FILENAME] = 'remove_boot_file'
333
334
335  # If we supplied args then make links for the supplied hosts
336  # else make links for class C-networks
337  #
338  if hostnames:
339        if not bootinfo.has_key(FILENAME):
340              bootinfo[FILENAME] = select_pxe_configfile()
341        hosts_links(hostnames,bootinfo)
342  else:
343        if bootinfo.has_key(INTERACTIVE):   
344                interactive(bootinfo)
345        else:
346                check_cmd_line(bootinfo)
347        manage_links(bootinfo)
348
349if __name__ == '__main__':
350  main()
Note: See TracBrowser for help on using the repository browser.