source: trunk/pxeconfig @ 74

Last change on this file since 74 was 74, checked in by bas, 17 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
RevLine 
[2]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#
[29]8# SVN info
9#  $Id: pxeconfig 74 2007-03-27 19:50:22Z bas $
[2]10#
[14]11# Copyright (C) 2002
[6]12#
[14]13# This file is part of the pxeconfig utils
[6]14#
[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.
[6]19#
[14]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.
[6]24#
[14]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#
[2]29"""
[19]30Usage: pxeconfig
[49]31
[19]32 [-d|--directory <pxe_config_dir>]
[53]33 [-f|--file <filename>] [hostname(s)]
[58]34 [-i|--interactive]
[53]35 [-n|--net <C-net> -s|--start <number> -e|--end <number> -f|--file <filename>]
36 [-r|--remove]
[24]37 [-V|--version]
[10]38
[19]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:
[2]44  1) Network address (Class C-network address only)
[9]45  2) Starting number
[2]46  3) Ending number
[9]47  4) Which PXE config file to use
[2]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
[3]65import getopt
[47]66import socket
[74]67import ConfigParser
[2]68
69# DEBUG
70#
71DEBUG=0
72
73# Constants
74#
[4]75PXE_CONF_DIR='/tftpboot/pxelinux.cfg'
[19]76NETWORK='network'
77FILENAME='filename'
78START='start'
79END='end'
[60]80VERSION='0.6.2'
[53]81REMOVE='remove'
[55]82INTERACTIVE='interactive'
[2]83
[55]84SHORTOPT_LIST='hVd:n:s:e:f:ri'
85LONGOPT_LIST=['help', 'version', 'directory=', 'net=', 'start=', 'end=', 'file=', 'remove', 'debug' , 'interactive' ]
[19]86
[74]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
[19]118def select_pxe_configfile():
[2]119  """
[9]120  Let user choose which pxeconfig file to use.
[2]121  """
122
[4]123  os.chdir(PXE_CONF_DIR)
[2]124
[9]125  # Try to determine to which file the default symlink points, and
126  # if it exists, remove it from the list.
[2]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:
[16]150    index = raw_input('Select a number: ')
[2]151    try:
152      index = int(index)
153    except ValueError:
154      index = len(files) + 1
155
156    # Is the user smart enough to select
[9]157    # the right value??
[2]158    #
159    if 0 < index <= len(files): break
160
161  return files[index-1]
162
[53]163def manage_links(dict):
[2]164  """
[4]165  Create the links in the PXE_CONF_DIR,
[9]166    list : A list containing: network hex address, pxe config file,
[2]167           start number, end number
168  """
[4]169  os.chdir(PXE_CONF_DIR)
[19]170  naddr = dict[NETWORK]
171  pxe_filename = dict[FILENAME]
172  for i in range(dict[START], dict[END]+1):
[2]173    haddr = '%s%02X' %(naddr, i)
[15]174
[53]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)
[15]186
[47]187def convert_network(net, prefix=''):
[2]188  """
[9]189  This function checks if the give network is a Class C-network and will
[2]190  convert the network address to a hex address if true.
191  """
192  d = string.split(net, '.')
193
194  if len(d) != 3:
[19]195    if prefix:
196      net = prefix + ' : ' + net
197     
198    print '%s is not a valid  C-class network address!!!' %net
[2]199    sys.exit(1)
200
[19]201
202  # Display right message for interactive/commandline
203  if prefix:
204    prefix = prefix + ' ' + net
205  else:
206    prefix = net
207
[2]208  # Check if we have valid network values
209  r = ''
210  for i in d:
[19]211    i = check_number(i, prefix)
[2]212    r = '%s%02X' %(r,i)
213
214  if DEBUG:
215    print r
216
217  return r
218
[19]219def check_number(number, prefix=''):
[2]220  """
221  This functions checks if the input is between 0 < number < 255:
222     number : is a string
[19]223     prefix ; a string that must be displayed if an error occurs
[2]224  """
225  try:
226    n = int(number)
227  except ValueError, detail:
[19]228    print prefix,detail
[2]229    sys.exit(1)
230
231  if 0 <= n <= 255:
232    return n
233  else:
[19]234
235    if prefix:
236      number = prefix +' : ' + number
237
[2]238    print '%s is not a valid number, must be between 0 and 255' %number
239    sys.exit(1)
240
[19]241def interactive(binfo):
[2]242
243  print __doc__
244
[9]245  network = raw_input('Give network address (xxx.xxx.xxx): ') 
[47]246  naddr = convert_network(network)
[2]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
[19]254  pxe_filename = select_pxe_configfile()
[2]255
[19]256  binfo[NETWORK] = naddr
257  binfo[START] = int(start)
258  binfo[END] = int(end)
259  binfo[FILENAME] = pxe_filename
[2]260
[19]261  if DEBUG:
262    print network, binfo
[2]263
[19]264def check_cmd_line(binfo):
[60]265  if len(binfo.keys()) != 5:
[19]266    print __doc__
[20]267    print 'Not enough arguments to create the links!!'
[19]268    sys.exit(1)
[6]269
[19]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):
[3]277  """
[47]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.
[3]283  """
[4]284  global PXE_CONF_DIR
[53]285  global DEBUG
[19]286
[3]287  try:
288    opts, args = getopt.getopt(argv[1:], SHORTOPT_LIST, LONGOPT_LIST)
289  except getopt.error, detail:
[20]290    print __doc__
[3]291    print detail
292    sys.exit(1)
293
[19]294  # Check given options
295  #
296  for opt,value in opts:
[55]297
[19]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)
[3]304
[55]305    elif opt in ['-i', '--interactive']:
306      binfo[INTERACTIVE] = 1
307
[19]308    elif opt in ['-n', '--net']:
309      network = value
[47]310      binfo[NETWORK] = convert_network(value, opt)
[4]311
[19]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
[24]321    elif opt in ['-V', '--version']:
[23]322      print VERSION
323      sys.exit(0)
324
[53]325    elif opt in ['-r', '--remove']:
326      binfo[REMOVE] = 1
327
328    elif opt in ['--debug']:
329      DEBUG = 1
330
[20]331    elif opt in ['-h', '--help']:
332      print __doc__
333      sys.exit(0)
[19]334
[51]335  if args:
336        return args
[20]337
[47]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])
[53]352                manage_links(binfo)
[47]353
354
[2]355def main():
[19]356  # A dictionary holding the boot info
357  #
358  bootinfo = {}
[53]359  bootinfo[REMOVE] = 0
[2]360
[74]361  project_name = None
362  configfile = 'pxeconfig.conf'
363  settings = ReadConfig(configfile, project_name)
364  print settings
365  sys.exit(1)
[47]366  hostnames = check_args(sys.argv, bootinfo)
367
[53]368  if bootinfo[REMOVE]:
369     bootinfo[FILENAME] = 'remove_boot_file'
[47]370
[53]371
[47]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):
[53]377              bootinfo[FILENAME] = select_pxe_configfile()
[47]378        hosts_links(hostnames,bootinfo)
[19]379  else:
[55]380        if bootinfo.has_key(INTERACTIVE):   
381                interactive(bootinfo)
382        else:
[47]383                check_cmd_line(bootinfo)
[53]384        manage_links(bootinfo)
[19]385
[2]386if __name__ == '__main__':
387  main()
Note: See TracBrowser for help on using the repository browser.