source: trunk/src/pxeconfig.py @ 168

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

Added a common module for daemon and client:

  • src/pxe_global.py.in

src/pxeconfig.py:

  • removed some statements that are moved to pxe_global.py.in
  • Property svn:executable set to *
  • Property svn:keywords set to Id URL
File size: 8.0 KB
Line 
1# set ts=4, sw=4
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.py 168 2009-08-19 07:51: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 [-f,--filename <name>] <hosts>
31
32Specifying hostnames:
33   To specify a range use the [] to indicate a range,
34   a couple of examples:
35
36   The first five nodes of rack 16
37          - gb-r16n[1-5]
38
39   The first five nodes and node 12 and 18 of rack 16 to 20
40      - gb-r[16-20]n[1-5,12,18]
41
42   The first five nodes de in rack 16 with padding enabled
43          - gb-r[16]n[01-5]
44
45The ranges ([]) are not only limited to numbers, letters can also be used.
46
47With this program you can configure which PXE configuration file a node
48will use when it boots.
49
50See following link for usage and examples:
51 - https://subtrac.sara.nl/oss/pxeconfig/wiki/PxeUsage
52"""
53
54import string
55import sys
56import os
57import glob
58import getopt
59import socket
60#import ConfigParser
61import re
62
63# import from the sara_python_modules
64import AdvancedParser
65from pxe_global import *
66
67# Constants
68#
69PXE_CONF_DIR = '/tftpboot/pxelinux.cfg'
70NETWORK      = 'network'
71START        = 'start'
72END          = 'end'
73VERSION      = '3.1.0'
74
75class PxeConfig(Exception):
76        def __init__(self, msg=''):
77                self.msg = msg
78                Exception.__init__(self, msg)
79
80        def __repr__(self):
81                return self.msg
82
83#def ReadConfig(file):
84#       """
85#       Parse the config file
86#       """
87#       if not os.path.isfile(file):
88#               error = 'File %s does not exist' %file
89#               raise PxeConfig, error
90#
91##      config = ConfigParser.RawConfigParser()
92#       try:
93#               config.read(file)
94#       except ConfigParser.MissingSectionHeaderError,detail:
95#               raise PxeConfig, detail
96#
97#       # Not yet uses
98#       #
99#       #projects = {}
100#       #for section in config.sections():
101##      #       projects[section] = {}
102#       #       for option in  config.options(section):
103#       #       projects[section][option] = config.get(section, option)
104#
105#       stanza = config.defaults()
106#       return stanza
107#
108
109def select_pxe_configfile():
110        """
111        Let user choose which pxeconfig file to use.
112        """ 
113       
114        os.chdir(PXE_CONF_DIR) 
115       
116        # Try to determine to which file the default symlink points, and
117        # if it exists, remove it from the list.
118        #
119        try:
120                default_file = os.readlink('default')
121        except OSError:
122                default_file = None
123        pass
124
125        files = glob.glob('default.*')
126        if not files:
127                error =  'There are no pxe config files starting with: default.'
128                raise PxeConfig, error
129
130        if default_file:
131                files.remove(default_file)
132
133        # sort the files
134        #
135        files.sort()
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): 
154                        break
155
156        return files[index-1]
157
158def manage_links(haddr, options):
159        """
160        Create the links in the PXE_CONF_DIR,
161        list : A list containing: network hex address, pxe config file,
162           start number, end number
163        """
164        if options.VERBOSE:
165                print 'manage_links(%s)' %(haddr)
166       
167        os.chdir(PXE_CONF_DIR)
168        pxe_filename = options.filename
169       
170        if options.REMOVE:
171                if options.DEBUG or options.DRY_RUN or options.VERBOSE:
172                        print 'removing %s/%s' %(PXE_CONF_DIR, haddr)
173
174                if os.path.exists(haddr) and not options.DRY_RUN:
175                        os.unlink(haddr)
176
177        else:
178                if options.DEBUG or options.DRY_RUN or options.VERBOSE:
179                        print 'linking %s to %s' %(haddr, pxe_filename)
180
181                if not options.DRY_RUN:
182                        if os.path.exists(haddr):
183                                os.unlink(haddr) 
184                        os.symlink(pxe_filename, haddr)
185
186def net_2_hex(net, options):
187        """
188        This function checks if the give network is a Class C-network and will
189        convert the network address to a hex address if true.
190        """
191        if options.DEBUG:
192                str = 'net_2_hex : %s' %(net)
193                print str
194       
195        d = string.split(net, '.') 
196        if len(d) != 3:
197                error = '%s is not a valid  C-class network address' %(net)
198                raise PxeConfig, error
199
200        # Check if we have valid network values
201        r = ''
202        for i in d:
203                r = '%s%02X' %(r, int(i))
204
205        if options.DEBUG:
206                print 'C-network converted to hex: ', r
207
208        return r
209
210def hosts_2_hex(hosts, options):
211        """
212        Convert hostname(s) to a net address that can be handled by manage_links function
213        """
214        if options.DEBUG:
215                str = 'host_2_hex: %s' %hosts
216                print str
217
218        for host in hosts:
219                try:
220                        addr = socket.gethostbyname(host)
221                except socket.error,detail:
222                        error =  '%s not an valid hostname: %s' %(host,detail)
223                        raise PxeConfig, error
224
225                net = string.splitfields(addr, '.')
226                cnet = string.joinfields(net[0:3], '.')
227
228                if options.SCRIPT_HOOK:
229                        if options.DEBUG or options.DRY_RUN or options.VERBOSE:
230                                print 'Executing client script hook: %s with arg: %s' %(options.SCRIPT_HOOK, addr)
231                        if not options.DRY_RUN:
232                                cmd = '%s %s' %(options.SCRIPT_HOOK, addr)
233
234                haddr = '%s%02X' %(net_2_hex(cnet, options), int(net[3])) 
235                manage_links(haddr, options)
236
237def add_options(p):
238        """
239        add the default options
240        """
241        p.set_defaults( 
242                DEBUG   = False,
243                VERBOSE = False,
244                DRY_RUN = False,
245                REMOVE  = False,
246                VERSION  = False,
247                SCRIPT_HOOK = False,
248        )
249
250        p.add_option('-d', '--debug',
251                action = 'store_true',
252                dest   = 'DEBUG',
253                help   = 'Toggle debug mode (default : False)'
254        )
255
256        p.add_option('-f', '--filename',
257                action = 'store',
258                dest   = 'filename',
259                help   = 'Specifies which PXE filename must be use'
260        )
261
262        p.add_option('-n', '--dry-run',
263                action = 'store_true',
264                dest   = 'DRY_RUN',
265                help   = 'Do not execute any command (default : False)'
266        )
267
268        p.add_option('-r', '--remove',
269                action = 'store_true',
270                dest   = 'REMOVE',
271                help   = 'Removes the PXE filename for a host(s)'
272        )
273
274        p.add_option('-v', '--verbose',
275                action = 'store_true',
276                dest   = 'VERBOSE',
277                help   = 'Make the program more verbose (default: False)'
278        )
279
280        p.add_option('-V', '--version',
281                action = 'store_true',
282                dest   = 'VERSION',
283                help   = 'Print the program version number and exit'
284        )
285
286def parser(argv, settings): 
287        """
288        Make use of sara advance parser module. It can handle regex in hostnames
289        """
290        parser = AdvancedParser.AdvancedParser(usage=__doc__)
291        add_options(parser)
292
293        options, args = parser.parse_args() 
294
295        if options.VERSION:
296                print VERSION
297                sys.exit(0) 
298
299        if not args:
300                parser.print_help()
301                sys.exit(0)
302
303        # debug can be specified by the command line or options file
304        #
305        if not options.DEBUG:
306                try:
307                        if settings['debug']:
308                                options.DEBUG = int(settings['debug'])
309                except KeyError:
310                        pass
311
312        # Only check if we have specified an pxeconfig file if we did not
313        # specify the REMOVE option
314        #
315        if not options.REMOVE:
316                if options.filename:
317                        if not os.path.isfile(os.path.join(PXE_CONF_DIR, options.filename)):
318                                error =  '%s: Filename does not exists' %(options.filename)
319                                raise PxeConfig, error
320                else:
321                        options.filename = select_pxe_configfile() 
322
323        # ...
324        if settings['client_script_hook']:
325                options.SCRIPT_HOOK = settings['client_script_hook']
326
327        if options.DEBUG:
328                print args, options
329
330        hosts_2_hex(args, options)
331
332
333def main():
334        # A dictionary holding the boot info
335        #
336        global PXE_CONF_DIR
337        settings = ReadConfig() 
338       
339        try:
340                PXE_CONF_DIR = settings['pxe_config_dir']
341
342        except KeyError:
343                pass 
344
345        PXE_CONF_DIR = os.path.realpath(PXE_CONF_DIR)
346        if not os.path.isdir(PXE_CONF_DIR):
347                error =  'pxeconfig directory: %s does not exists' %(PXE_CONF_DIR)
348                raise PxeConfig, error
349
350        parser(sys.argv, settings)
351
352       
353if __name__ == '__main__':
354        try:
355                main()
356        except PxeConfig, detail:
357                print detail
358                sys.exit(1)
Note: See TracBrowser for help on using the repository browser.