source: trunk/src/pxeconfig.py

Last change on this file was 206, checked in by bas, 11 years ago

added a new option to skip hostname lookup failures

  • Property svn:executable set to *
  • Property svn:keywords set to Id URL
File size: 10.4 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 206 2012-12-18 11:53:42Z 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      = '4.2.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, ip_addr, 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 options.SCRIPT_HOOK_REMOVE and ip_addr:
144                print 'Executing client script hook remove : %s with arg: %s' %(options.SCRIPT_HOOK_REMOVE, ip_addr)
145
146        if os.path.exists(haddr) and not options.DRY_RUN:
147            os.unlink(haddr)
148
149    else:
150
151        if options.DEBUG or options.DRY_RUN or options.VERBOSE:
152            print 'linking %s to %s' %(haddr, pxe_filename)
153
154            if options.SCRIPT_HOOK_ADD and ip_addr:
155                print 'Executing client script hook add : %s with arg: %s' %(options.SCRIPT_HOOK_ADD, ip_addr)
156
157        if not options.DRY_RUN:
158            if os.path.exists(haddr):
159                os.unlink(haddr) 
160            os.symlink(pxe_filename, haddr)
161   
162            if options.SCRIPT_HOOK_ADD and ip_addr:
163                cmd = '%s %s' %(options.SCRIPT_HOOK_ADD, ip_addr)
164                os.system(cmd)
165
166def net_2_hex(net, options):
167    """
168    This function checks if the give network is a Class C-network and will
169    convert the network address to a hex address if true.
170    """
171    if options.DEBUG:
172        str = 'net_2_hex : %s' %(net)
173        print str
174   
175    d = string.split(net, '.') 
176    if len(d) != 3:
177        error = '%s is not a valid  C-class network address' %(net)
178        raise PxeConfig, error
179
180    # Check if we have valid network values
181    r = ''
182    for i in d:
183        r = '%s%02X' %(r, int(i))
184
185    if options.DEBUG:
186        print 'C-network converted to hex: ', r
187
188    return r
189
190def host_2_hex(host, options):
191    """
192    Convert hostname(s) to a net address that can be handled by manage_links function
193    """
194    if options.DEBUG:
195        str = 'host_2_hex: %s' %hosts
196        print str
197
198    try:
199        addr = socket.gethostbyname(host)
200    except socket.error,detail:
201        error =  '%s not an valid hostname: %s' %(host,detail)
202        raise PxeConfig, error
203
204    net = string.splitfields(addr, '.')
205    cnet = string.joinfields(net[0:3], '.')
206
207
208    haddr = '%s%02X' %(net_2_hex(cnet, options), int(net[3])) 
209    manage_links(haddr, addr, options)
210
211
212def mac_2_hex(mac_addr, options):
213    """
214    Convert mac address to pxeconfig address
215    """
216    if options.DEBUG:
217        str = 'mac_2_hex: %s' %mac_addr
218        print mac_addr
219
220    haddr = '01-%s' %(mac_addr.replace(':', '-').lower())
221    manage_links(haddr, None, options)
222
223def add_options(p):
224    """
225    add the default options
226    """
227    p.set_defaults( 
228        DEBUG   = False,
229        VERBOSE = False,
230        DRY_RUN = False,
231        REMOVE  = False,
232        VERSION  = False,
233        SCRIPT_HOOK_ADD = False,
234        SCRIPT_HOOK_REMOVE = False,
235        SKIP_HOSTNAME_ERRORS = False,
236    )
237
238    p.add_option('-d', '--debug',
239        action = 'store_true',
240        dest   = 'DEBUG',
241        help   = 'Toggle debug mode (default : False)'
242    )
243
244    p.add_option('-f', '--filename',
245        action = 'store',
246        dest   = 'filename',
247        help   = 'Specifies which PXE filename must be use'
248    )
249
250    p.add_option('-n', '--dry-run',
251        action = 'store_true',
252        dest   = 'DRY_RUN',
253        help   = 'Do not execute any command (default : False)'
254    )
255
256    p.add_option('-r', '--remove',
257        action = 'store_true',
258        dest   = 'REMOVE',
259        help   = 'Removes the PXE filename for a host(s)'
260    )
261
262    p.add_option('-v', '--verbose',
263        action = 'store_true',
264        dest   = 'VERBOSE',
265        help   = 'Make the program more verbose (default: False)'
266    )
267
268    p.add_option('-H', '--skip-hostname-lookup-error',
269        action = 'store_true',
270        dest   = 'SKIP_HOSTNAME_LOOKUP_ERROR',
271        help   = 'When hostname lookup fails, skip it (default: False)'
272    )
273
274    p.add_option('-V', '--version',
275        action = 'store_true',
276        dest   = 'VERSION',
277        help   = 'Print the program version number and exit'
278    )
279
280def parser(argv, config, defaults): 
281    """
282    Make use of sara advance parser module. It can handle regex in hostnames
283    """
284    parser = AdvancedParser.AdvancedParser(usage=__doc__)
285    add_options(parser)
286
287    options, args = parser.parse_args() 
288
289    if options.VERSION:
290        print VERSION
291        sys.exit(0) 
292
293    if not args:
294        parser.print_help()
295        sys.exit(0)
296
297    # debug can be specified by the command line or options file
298    #
299    if not options.DEBUG:
300        try:
301            if defaults['debug']:
302                options.DEBUG = int(defaults['debug'])
303        except KeyError:
304            pass
305
306    # Only check if we have specified an pxeconfig file if we did not
307    # specify the REMOVE option
308    #
309    if not options.REMOVE:
310        if options.filename:
311            if not os.path.isfile(os.path.join(PXE_CONF_DIR, options.filename)):
312                error =  '%s: Filename does not exists' %(options.filename)
313                raise PxeConfig, error
314        else:
315            options.filename = select_pxe_configfile() 
316
317
318    if not options.SKIP_HOSTNAME_LOOKUP_ERROR:
319        try:
320            options.SKIP_HOSTNAME_LOOKUP_ERROR = defaults['skip_hostname_lookup_error']
321        except KeyError, detail:
322            pass
323
324    ## This will be obsoleted by client_script_hook_add
325    #
326    try:
327        options.SCRIPT_HOOK_ADD = defaults['client_script_hook']
328    except KeyError, detail:
329        pass
330
331    try:
332        options.SCRIPT_HOOK_ADD = defaults['client_script_hook_add']
333    except KeyError, detail:
334        pass
335
336    try:
337        options.SCRIPT_HOOK_REMOVE = defaults['client_script_hook_remove']
338    except KeyError, detail:
339        pass
340
341    if options.DEBUG:
342        print args, options
343
344    ##
345    # Are the hosts wiht only mac addresses defined in the configuration file
346    # or specified on the command line
347    #
348    mac_addr_re = re.compile('([a-fA-F0-9]{2}[:|\-]?){6}')
349
350    for host in args:
351        if host in config.sections():
352            mac_addr = config.get(host, 'mac_address') 
353            mac_2_hex(mac_addr, options)
354
355        elif mac_addr_re.search(host):
356            mac_2_hex(host, options)
357        else:
358            try:
359                host_2_hex(host, options)
360            except PxeConfig, detail:
361
362                if options.SKIP_HOSTNAME_LOOKUP_ERROR:
363                    print 'Skipping Hostname lookup failed for: %s' %(host)
364                    continue
365                else:
366                    print detail
367                    sys.exit(1)
368
369def main():
370    # A dictionary holding the boot info
371    #
372    global PXE_CONF_DIR
373    parser_config, default_settings = ReadConfig() 
374   
375    try:
376        PXE_CONF_DIR = default_settings['pxe_config_dir']
377
378    except KeyError:
379        pass 
380
381    PXE_CONF_DIR = os.path.realpath(PXE_CONF_DIR)
382    if not os.path.isdir(PXE_CONF_DIR):
383        error =  'pxeconfig directory: %s does not exists' %(PXE_CONF_DIR)
384        raise PxeConfig, error
385
386    parser(sys.argv, parser_config, default_settings)
387
388   
389if __name__ == '__main__':
390    try:
391        main()
392    except PxeConfig, detail:
393        print detail
394        sys.exit(1)
Note: See TracBrowser for help on using the repository browser.