source: trunk/src/pxeconfig.py @ 200

Last change on this file since 200 was 194, checked in by bas, 13 years ago

src/pxeconfig.py:

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