source: trunk/src/pxeconfig.py @ 186

Last change on this file since 186 was 186, checked in by bas, 14 years ago

Added mac address support for environments that have dynamic ip's. Depended on arp command

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