source: trunk/pxeconfig/pxeconfig @ 57

Last change on this file since 57 was 57, checked in by bas, 19 years ago

pxeconfig:

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