source: trunk/pxeconfig @ 74

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

pxeconfig:

  • Added config file support pxeconfig.conf
  • Property keywords set to Id
  • Property svn:keywords set to Id
File size: 8.8 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 74 2007-03-27 19:50:22Z 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
67import ConfigParser
68
69# DEBUG
70#
71DEBUG=0
72
73# Constants
74#
75PXE_CONF_DIR='/tftpboot/pxelinux.cfg'
76NETWORK='network'
77FILENAME='filename'
78START='start'
79END='end'
80VERSION='0.6.2'
81REMOVE='remove'
82INTERACTIVE='interactive'
83
84SHORTOPT_LIST='hVd:n:s:e:f:ri'
85LONGOPT_LIST=['help', 'version', 'directory=', 'net=', 'start=', 'end=', 'file=', 'remove', 'debug' , 'interactive' ]
86
87def ReadConfig(file, name):
88        """
89        Parse the config file
90        """
91        if not os.path.isfile(file):
92                print 'File %s does not exist' %file
93                sys.exit(1)
94
95        config = ConfigParser.ConfigParser()
96        try:
97                config.read(file)
98        except ConfigParser.MissingSectionHeaderError,detail:
99                print detail
100                sys.exit(1)
101
102        # Use given project name else use defaults
103        #
104        if name:
105                if not config.has_section(name):
106                        print "Not a valid project name: %s" %name
107                        print "Valid names: %s" %config.sections()
108                        sys.exit(1) 
109
110                project =  dict()
111                for option in  config.options(name):
112                        project[option] = config.get(name, option) 
113        else:
114                project = config.defaults() 
115
116        return project
117
118def select_pxe_configfile():
119  """
120  Let user choose which pxeconfig file to use.
121  """
122
123  os.chdir(PXE_CONF_DIR)
124
125  # Try to determine to which file the default symlink points, and
126  # if it exists, remove it from the list.
127  #
128  try:
129    default_file = os.readlink('default')
130  except OSError:
131    default_file = None
132    pass
133
134  files = glob.glob('default.*')
135  if not files:
136    print 'There are no pxe config files starting with: default.'
137    sys.exit(1)
138
139  if default_file:
140    files.remove(default_file)
141 
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, prefix=''):
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 prefix,detail
229    sys.exit(1)
230
231  if 0 <= n <= 255:
232    return n
233  else:
234
235    if prefix:
236      number = prefix +' : ' + number
237
238    print '%s is not a valid number, must be between 0 and 255' %number
239    sys.exit(1)
240
241def interactive(binfo):
242
243  print __doc__
244
245  network = raw_input('Give network address (xxx.xxx.xxx): ') 
246  naddr = convert_network(network)
247
248  start = raw_input('Starting number: ') 
249  start = check_number(start)
250
251  end = raw_input('Ending number: ') 
252  end = check_number(end)
253
254  pxe_filename = select_pxe_configfile()
255
256  binfo[NETWORK] = naddr
257  binfo[START] = int(start)
258  binfo[END] = int(end)
259  binfo[FILENAME] = pxe_filename
260
261  if DEBUG:
262    print network, binfo
263
264def check_cmd_line(binfo):
265  if len(binfo.keys()) != 5:
266    print __doc__
267    print 'Not enough arguments to create the links!!'
268    sys.exit(1)
269
270  # check_filename
271  #
272  if not os.path.isfile(os.path.join(PXE_CONF_DIR, binfo[FILENAME])):
273    print '%s: Filename does not exists' %binfo[FILENAME]
274    sys.exit(1)
275
276def check_args(argv, binfo):
277  """
278  This function parses the command line options and returns the rest as
279  an list of hostnames:
280    argv     : a list of command line options.
281    binfo    : returning a dict with the netinfo. if used non-interactively
282    hostnames: the rest of the command lines options that are not-parseble.
283  """
284  global PXE_CONF_DIR
285  global DEBUG
286
287  try:
288    opts, args = getopt.getopt(argv[1:], SHORTOPT_LIST, LONGOPT_LIST)
289  except getopt.error, detail:
290    print __doc__
291    print detail
292    sys.exit(1)
293
294  # Check given options
295  #
296  for opt,value in opts:
297
298    if opt in ['-d', '--directory']:
299      if os.path.isdir(value):
300        PXE_CONF_DIR = value
301      else:
302        print 'Directory %s does not exists\n' %value
303        sys.exit(1)
304
305    elif opt in ['-i', '--interactive']:
306      binfo[INTERACTIVE] = 1
307
308    elif opt in ['-n', '--net']:
309      network = value
310      binfo[NETWORK] = convert_network(value, opt)
311
312    elif opt in ['-s', '--start']:
313      binfo[START] = check_number(value, opt)
314
315    elif opt in ['-e', '--end']:
316      binfo[END] = check_number(value, opt)
317
318    elif opt in ['-f', '--file']:
319      binfo[FILENAME] = value
320
321    elif opt in ['-V', '--version']:
322      print VERSION
323      sys.exit(0)
324
325    elif opt in ['-r', '--remove']:
326      binfo[REMOVE] = 1
327
328    elif opt in ['--debug']:
329      DEBUG = 1
330
331    elif opt in ['-h', '--help']:
332      print __doc__
333      sys.exit(0)
334
335  if args:
336        return args
337
338def hosts_links(hosts, binfo):
339        for host in hosts:
340                try:
341                        addr = socket.gethostbyname(host)
342                except socket.error,detail:
343                        print '%s not an valid hostname: %s' %(host,detail)
344                        sys.exit(1)
345                       
346                net = string.splitfields(addr, '.')
347                cnet = string.joinfields(net[0:3], '.')
348
349                binfo[NETWORK] = convert_network(cnet)
350                binfo[START] = int(net[3])
351                binfo[END] =  int(net[3])
352                manage_links(binfo)
353
354
355def main():
356  # A dictionary holding the boot info
357  #
358  bootinfo = {}
359  bootinfo[REMOVE] = 0
360
361  project_name = None
362  configfile = 'pxeconfig.conf'
363  settings = ReadConfig(configfile, project_name)
364  print settings
365  sys.exit(1)
366  hostnames = check_args(sys.argv, bootinfo)
367
368  if bootinfo[REMOVE]:
369     bootinfo[FILENAME] = 'remove_boot_file'
370
371
372  # If we supplied args then make links for the supplied hosts
373  # else make links for class C-networks
374  #
375  if hostnames:
376        if not bootinfo.has_key(FILENAME):
377              bootinfo[FILENAME] = select_pxe_configfile()
378        hosts_links(hostnames,bootinfo)
379  else:
380        if bootinfo.has_key(INTERACTIVE):   
381                interactive(bootinfo)
382        else:
383                check_cmd_line(bootinfo)
384        manage_links(bootinfo)
385
386if __name__ == '__main__':
387  main()
Note: See TracBrowser for help on using the repository browser.