]> git.proxmox.com Git - mirror_ifupdown2.git/blob - sbin/ifupdown2
Update README.rst (#11)
[mirror_ifupdown2.git] / sbin / ifupdown2
1 #!/usr/bin/python
2 #
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6 # ifupdown --
7 # tool to configure network interfaces
8 #
9 import sys
10 import os
11 import argcomplete
12 import argparse
13 import ConfigParser
14 import StringIO
15 import logging
16 import logging.handlers
17 import resource
18 from ifupdown.ifupdownmain import *
19 from ifupdown.utils import *
20
21 IFUPDOWN2_VERSION = '1.1-cl3u9'
22
23 lockfile="/run/network/.lock"
24 configfile="/etc/network/ifupdown2/ifupdown2.conf"
25 configmap_g=None
26 logger = None
27 interfacesfileiobuf=None
28 interfacesfilename=None
29 ENVPATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
30
31 def run_up(args):
32 logger.debug('args = %s' %str(args))
33
34 try:
35 iflist = args.iflist
36 if len(args.iflist) == 0:
37 iflist = None
38 logger.debug('creating ifupdown object ..')
39 cachearg=(False if (iflist or args.nocache or
40 args.perfmode or args.noact)
41 else True)
42 ifupdown_handle = ifupdownMain(config=configmap_g,
43 force=args.force,
44 withdepends=args.withdepends,
45 perfmode=args.perfmode,
46 dryrun=args.noact,
47 cache=cachearg,
48 addons_enable=not args.noaddons,
49 statemanager_enable=not args.noaddons,
50 interfacesfile=interfacesfilename,
51 interfacesfileiobuf=interfacesfileiobuf,
52 interfacesfileformat=args.interfacesfileformat)
53 if args.noaddons:
54 ifupdown_handle.up(['up'], args.all, args.CLASS, iflist,
55 excludepats=args.excludepats,
56 printdependency=args.printdependency,
57 syntaxcheck=args.syntaxcheck, type=args.type,
58 skipupperifaces=args.skipupperifaces)
59 else:
60 ifupdown_handle.up(['pre-up', 'up', 'post-up'],
61 args.all, args.CLASS, iflist,
62 excludepats=args.excludepats,
63 printdependency=args.printdependency,
64 syntaxcheck=args.syntaxcheck, type=args.type,
65 skipupperifaces=args.skipupperifaces)
66 except:
67 raise
68
69 def run_down(args):
70 logger.debug('args = %s' %str(args))
71
72 try:
73 iflist = args.iflist
74 logger.debug('creating ifupdown object ..')
75 ifupdown_handle = ifupdownMain(config=configmap_g, force=args.force,
76 withdepends=args.withdepends,
77 perfmode=args.perfmode,
78 dryrun=args.noact,
79 addons_enable=not args.noaddons,
80 statemanager_enable=not args.noaddons,
81 interfacesfile=interfacesfilename,
82 interfacesfileiobuf=interfacesfileiobuf,
83 interfacesfileformat=args.interfacesfileformat)
84
85 ifupdown_handle.down(['pre-down', 'down', 'post-down'],
86 args.all, args.CLASS, iflist,
87 excludepats=args.excludepats,
88 printdependency=args.printdependency,
89 usecurrentconfig=args.usecurrentconfig,
90 type=args.type)
91 except:
92 raise
93
94 def run_query(args):
95 logger.debug('args = %s' %str(args))
96
97 try:
98 iflist = args.iflist
99 if args.checkcurr:
100 qop='query-checkcurr'
101 elif args.running:
102 qop='query-running'
103 elif args.raw:
104 qop='query-raw'
105 elif args.syntaxhelp:
106 qop = 'query-syntax'
107 elif args.printdependency:
108 qop = 'query-dependency'
109 elif args.printsavedstate:
110 qop = 'query-savedstate'
111 else:
112 qop='query'
113 cachearg=(False if (iflist or args.nocache or
114 args.perfmode or args.syntaxhelp or
115 (qop != 'query-checkcurr' and
116 qop != 'query-running')) else True)
117 if not iflist and qop == 'query-running':
118 iflist = [i for i in os.listdir('/sys/class/net/')
119 if os.path.isdir('/sys/class/net/%s' %i)]
120 logger.debug('creating ifupdown object ..')
121 ifupdown_handle = ifupdownMain(config=configmap_g,
122 withdepends=args.withdepends,
123 perfmode=args.perfmode,
124 cache=cachearg,
125 interfacesfile=interfacesfilename,
126 interfacesfileiobuf=interfacesfileiobuf,
127 interfacesfileformat=args.interfacesfileformat,
128 withdefaults=args.withdefaults)
129 # list implies all auto interfaces (this is how ifupdown behaves)
130 if args.list:
131 args.all = True
132 ifupdown_handle.query([qop], args.all, args.list, args.CLASS, iflist,
133 excludepats=args.excludepats,
134 printdependency=args.printdependency,
135 format=args.format, type=args.type)
136 except:
137 raise
138
139 def run_reload(args):
140 logger.debug('args = %s' %str(args))
141
142 try:
143 logger.debug('creating ifupdown object ..')
144 ifupdown_handle = ifupdownMain(config=configmap_g,
145 interfacesfile=interfacesfilename,
146 withdepends=args.withdepends,
147 perfmode=args.perfmode,
148 dryrun=args.noact)
149 ifupdown_handle.reload(['pre-up', 'up', 'post-up'],
150 ['pre-down', 'down', 'post-down'],
151 auto=args.all, allow=args.CLASS, ifacenames=None,
152 excludepats=args.excludepats,
153 usecurrentconfig=args.usecurrentconfig,
154 syntaxcheck=args.syntaxcheck,
155 currentlyup=args.currentlyup)
156 except:
157 raise
158
159 def init(args):
160 global logger
161 global interfacesfileiobuf
162 global interfacesfilename
163
164 log_level = logging.WARNING
165 if args.verbose:
166 log_level = logging.INFO
167 if args.debug:
168 log_level = logging.DEBUG
169
170 try:
171 if hasattr(args, 'syslog') and args.syslog:
172 root_logger = logging.getLogger()
173 syslog_handler = logging.handlers.SysLogHandler(address='/dev/log',
174 facility=logging.handlers.SysLogHandler.LOG_DAEMON)
175 logging.addLevelName(logging.ERROR, 'error')
176 logging.addLevelName(logging.WARNING, 'warning')
177 logging.addLevelName(logging.DEBUG, 'debug')
178 logging.addLevelName(logging.INFO, 'info')
179 root_logger.setLevel(log_level)
180 syslog_handler.setFormatter(logging.Formatter(
181 '%(name)s: %(levelname)s: %(message)s'))
182 root_logger.addHandler(syslog_handler)
183 logger = logging.getLogger('ifupdown')
184 else:
185 logging.basicConfig(level=log_level,
186 format='%(levelname)s: %(message)s')
187 logging.addLevelName(logging.ERROR, 'error')
188 logging.addLevelName(logging.WARNING, 'warning')
189 logging.addLevelName(logging.DEBUG, 'debug')
190 logging.addLevelName(logging.INFO, 'info')
191 logger = logging.getLogger('ifupdown')
192 except:
193 raise
194
195 if hasattr(args, 'interfacesfile') and args.interfacesfile != None:
196 # Check to see if -i option is allowed by config file
197 # But for ifquery, we will not check this
198 if (not sys.argv[0].endswith('ifquery') and
199 configmap_g.get('disable_cli_interfacesfile','0') == '1'):
200 logger.error('disable_cli_interfacesfile is set so users '
201 'not allowed to specify interfaces file on cli.')
202 exit(1)
203 if args.interfacesfile == '-':
204 # If interfaces file is stdin, read
205 interfacesfileiobuf = sys.stdin.read()
206 else:
207 interfacesfilename = args.interfacesfile
208 else:
209 # if the ifupdown2 config file does not have it, default to standard
210 interfacesfilename = configmap_g.get('default_interfaces_configfile',
211 '/etc/network/interfaces')
212
213
214
215
216 def deinit():
217 {}
218
219 def update_argparser(argparser):
220 """ base parser, common to all commands """
221
222 argparser.add_argument('-a', '--all', action='store_true', required=False,
223 help='process all interfaces marked \"auto\"')
224 argparser.add_argument('iflist', metavar='IFACE',
225 nargs='*', help='interface list separated by spaces. ' +
226 'IFACE list is mutually exclusive with -a option.')
227 argparser.add_argument('-v', '--verbose', dest='verbose',
228 action='store_true', help='verbose')
229 argparser.add_argument('-d', '--debug', dest='debug',
230 action='store_true',
231 help='output debug info')
232 argparser.add_argument('-q', '--quiet', dest='quiet',
233 action='store_true',
234 help=argparse.SUPPRESS)
235 argparser.add_argument('--allow', dest='CLASS',
236 help='ignore non-\"allow-CLASS\" interfaces')
237 argparser.add_argument('-w', '--with-depends', dest='withdepends',
238 action='store_true', help='run with all dependent interfaces.'+
239 ' This option is redundant when \'-a\' is specified. With ' +
240 '\'-a\' interfaces are always executed in dependency order')
241 argparser.add_argument('--perfmode', dest='perfmode',
242 action='store_true', help=argparse.SUPPRESS)
243 #argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
244 # default=-1, choices=range(1,12), help=argparse.SUPPRESS)
245 argparser.add_argument('--nocache', dest='nocache', action='store_true',
246 help=argparse.SUPPRESS)
247 argparser.add_argument('-X', '--exclude', dest='excludepats',
248 action='append',
249 help='Exclude interfaces from the list of interfaces' +
250 ' to operate on. Can be specified multiple times.')
251 argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
252 default=None,
253 help='Specify interfaces file instead of file defined ' +
254 'in ifupdown2.conf file')
255 argparser.add_argument('-t', '--interfaces-format',
256 dest='interfacesfileformat',
257 default='native',
258 choices=['native', 'json'],
259 help='interfaces file format')
260 argparser.add_argument('-T', '--type',
261 dest='type',
262 default=None,
263 choices=['iface', 'vlan'],
264 help='type of interface entry (iface or vlan).' +
265 'This option can be used in case of ambiguity between ' +
266 'a vlan interface and an iface interface of the same name')
267
268 def update_ifupdown_argparser(argparser):
269 """ common arg parser for ifup and ifdown """
270 argparser.add_argument('-f', '--force', dest='force',
271 action='store_true',
272 help='force run all operations')
273 argparser.add_argument('-l', '--syslog', dest='syslog',
274 action='store_true',
275 help=argparse.SUPPRESS)
276 group = argparser.add_mutually_exclusive_group(required=False)
277 group.add_argument('-n', '--no-act', dest='noact',
278 action='store_true', help='print out what would happen,' +
279 'but don\'t do it')
280 group.add_argument('-p', '--print-dependency',
281 dest='printdependency', choices=['list', 'dot'],
282 help='print iface dependency')
283 group.add_argument('--no-scripts', '--admin-state',
284 dest='noaddons', action='store_true',
285 help='dont run any addon modules/scripts. Only bring the ' +
286 'interface administratively up/down')
287
288 def update_ifup_argparser(argparser):
289 argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
290 action='store_true',
291 help='Only run the interfaces file parser')
292 argparser.add_argument('-k', '--skip-upperifaces', dest='skipupperifaces',
293 action='store_true',
294 help='ifup by default tries to add newly created interfaces' +
295 ' into its upper/parent interfaces. Eg. if a bridge port is' +
296 ' created as a result of ifup on the port, ifup automatically' +
297 ' adds the port to the bridge. This option can be used to ' +
298 'disable this default behaviour')
299 update_ifupdown_argparser(argparser)
300
301 def update_ifdown_argparser(argparser):
302 update_ifupdown_argparser(argparser)
303 argparser.add_argument('-u', '--use-current-config',
304 dest='usecurrentconfig', action='store_true',
305 help='By default ifdown looks at the saved state for ' +
306 'interfaces to bring down. This option allows ifdown to ' +
307 'look at the current interfaces file. Useful when your ' +
308 'state file is corrupted or you want down to use the latest '
309 'from the interfaces file')
310
311 def update_ifquery_argparser(argparser):
312 """ arg parser for ifquery options """
313
314 # -l is same as '-a', only here for backward compatibility
315 argparser.add_argument('-l', '--list', action='store_true', dest='list',
316 help='list all matching known interfaces')
317 group = argparser.add_mutually_exclusive_group(required=False)
318 group.add_argument('-r', '--running', dest='running',
319 action='store_true',
320 help='query running state of an interface')
321 group.add_argument('-c', '--check', dest='checkcurr',
322 action='store_true',
323 help='check interface file contents against ' +
324 'running state of an interface')
325 group.add_argument('-x', '--raw', action='store_true', dest='raw',
326 help='print raw config file entries')
327 group.add_argument('--print-savedstate', action='store_true',
328 dest='printsavedstate',
329 help=argparse.SUPPRESS)
330 argparser.add_argument('-o', '--format', dest='format', default='native',
331 choices=['native', 'json'],
332 help='interface display format')
333 argparser.add_argument('-p', '--print-dependency',
334 dest='printdependency', choices=['list', 'dot'],
335 help='print interface dependency')
336 argparser.add_argument('-s', '--syntax-help', action='store_true',
337 dest='syntaxhelp',
338 help='print supported interface config syntax')
339 argparser.add_argument('--with-defaults', action='store_true',
340 dest='withdefaults',
341 help='check policy default file contents, ' +
342 'for unconfigured attributes, against ' +
343 'running state of an interface')
344
345 def update_ifreload_argparser(argparser):
346 """ parser for ifreload """
347 group = argparser.add_mutually_exclusive_group(required=True)
348 group.add_argument('-a', '--all', action='store_true',
349 help='process all interfaces marked \"auto\"')
350 group.add_argument('-c', '--currently-up', dest='currentlyup',
351 action='store_true',
352 help='only reload auto and other interfaces that are ' +
353 'currently up')
354 group.add_argument('--allow', dest='CLASS',
355 help='ignore non-\"allow-CLASS\" interfaces')
356 argparser.add_argument('iflist', metavar='IFACE',
357 nargs='*', help=argparse.SUPPRESS)
358 argparser.add_argument('-n', '--no-act', dest='noact',
359 action='store_true', help='print out what would happen,' +
360 'but don\'t do it')
361 argparser.add_argument('-v', '--verbose', dest='verbose',
362 action='store_true', help='verbose')
363 argparser.add_argument('-d', '--debug', dest='debug',
364 action='store_true',
365 help='output debug info')
366 argparser.add_argument('-w', '--with-depends', dest='withdepends',
367 action='store_true', help=argparse.SUPPRESS)
368 argparser.add_argument('--perfmode', dest='perfmode',
369 action='store_true', help=argparse.SUPPRESS)
370 argparser.add_argument('--nocache', dest='nocache', action='store_true',
371 help=argparse.SUPPRESS)
372 argparser.add_argument('-X', '--exclude', dest='excludepats',
373 action='append',
374 help=argparse.SUPPRESS)
375 #argparser.add_argument('-j', '--jobs', dest='jobs', type=int,
376 # default=-1, choices=range(1,12), help=argparse.SUPPRESS)
377 #argparser.add_argument('-i', '--interfaces', dest='interfacesfile',
378 # default='/etc/network/interfaces',
379 # help='use interfaces file instead of default ' +
380 # '/etc/network/interfaces')
381 argparser.add_argument('-u', '--use-current-config',
382 dest='usecurrentconfig', action='store_true',
383 help='By default ifreload looks at saved state for ' +
384 'interfaces to bring down. With this option ifreload will'
385 ' only look at the current interfaces file. Useful when your ' +
386 'state file is corrupted or you want down to use the latest '
387 'from the interfaces file')
388 argparser.add_argument('-l', '--syslog', dest='syslog',
389 action='store_true',
390 help=argparse.SUPPRESS)
391 argparser.add_argument('-f', '--force', dest='force',
392 action='store_true',
393 help='force run all operations')
394 argparser.add_argument('-s', '--syntax-check', dest='syntaxcheck',
395 action='store_true',
396 help='Only run the interfaces file parser')
397
398 def update_common_argparser(argparser):
399 ''' general parsing rules '''
400
401 argparser.add_argument('-V', '--version',
402 action='version',
403 version='ifupdown2:%(prog)s ' + IFUPDOWN2_VERSION,
404 help='display current ifupdown2 version')
405
406 def parse_args(argsv, op):
407 if op == 'query':
408 descr = 'query interfaces (all or interface list)'
409 elif op == 'reload':
410 descr = 'reload interface configuration.\n\n \
411 ifreload downs interfaces that were removed\n \
412 from the interfaces file and subsequently runs ifup\n \
413 on all interfaces. ifreload is non-disruptive. It will fix\n \
414 running config to match what is configured in the interfaces\n \
415 file without bringing the interface down. It may flap an interface\n\
416 for some attribute changes. Please see man ifreload\n \
417 for details.'
418 else:
419 descr = 'interface management'
420 argparser = argparse.ArgumentParser(description=descr)
421 if op == 'reload':
422 update_ifreload_argparser(argparser)
423 else:
424 update_argparser(argparser)
425 if op == 'up':
426 update_ifup_argparser(argparser)
427 elif op == 'down':
428 update_ifdown_argparser(argparser)
429 elif op == 'query':
430 update_ifquery_argparser(argparser)
431 update_common_argparser(argparser)
432 argcomplete.autocomplete(argparser)
433 return argparser.parse_args(argsv)
434
435 handlers = {'up' : run_up,
436 'down' : run_down,
437 'query' : run_query,
438 'reload' : run_reload }
439
440 def validate_args(op, args):
441 #if op == 'up' and args.syntaxcheck:
442 # if args.iflist or args.all:
443 # print 'ignoring interface options ..'
444 # return True
445 if op == 'query' and (args.syntaxhelp or args.list):
446 return True
447 if op == 'reload':
448 if not args.all and not args.currentlyup and not args.CLASS:
449 print '\'-a\' or \'-c\' or \'-allow\' option is required'
450 return False
451 elif (not args.iflist and
452 not args.all and not args.CLASS):
453 print '\'-a\' option or interface list are required'
454 return False
455 if args.iflist and args.all:
456 print '\'-a\' option and interface list are mutually exclusive'
457 return False
458 if op != 'reload' and args.CLASS and (args.all or args.iflist):
459 print ('\'--allow\' option is mutually exclusive ' +
460 'with interface list and \'-a\'')
461 return False
462 return True
463
464 def read_config(args):
465 global configmap_g
466
467 with open(configfile, 'r') as f:
468 config = f.read()
469 configStr = '[ifupdown2]\n' + config
470 configFP = StringIO.StringIO(configStr)
471 parser = ConfigParser.RawConfigParser()
472 parser.readfp(configFP)
473 configmap_g = dict(parser.items('ifupdown2'))
474
475 # Preprocess config map
476 configval = configmap_g.get('multiple_vlan_aware_bridge_support', '0')
477 if configval == '0':
478 # if multiple bridges not allowed, set the bridge-vlan-aware
479 # attribute in the 'no_repeats' config, so that the ifupdownmain
480 # module can catch it appropriately
481 configmap_g['no_repeats'] = {'bridge-vlan-aware' : 'yes'}
482
483
484 configval = configmap_g.get('link_master_slave', '0')
485 if configval == '1':
486 # link_master_slave is only valid when all is set
487 if hasattr(args, 'all') and not args.all:
488 configmap_g['link_master_slave'] = '0'
489
490 configval = configmap_g.get('delay_admin_state_change', '0')
491 if configval == '1':
492 # reset link_master_slave if delay_admin_state_change is on
493 configmap_g['link_master_slave'] = '0'
494
495 def main(argv):
496 """ main function """
497 args = None
498 try:
499 op = None
500 if argv[0].endswith('ifup'):
501 op = 'up'
502 elif argv[0].endswith('ifdown'):
503 op = 'down'
504 elif argv[0].endswith('ifquery'):
505 op = 'query'
506 elif argv[0].endswith('ifreload'):
507 op = 'reload'
508 else:
509 print ('Unexpected executable.' +
510 ' Should be \'ifup\' or \'ifdown\' or \'ifquery\'')
511 exit(1)
512 # Command line arg parser
513 args = parse_args(argv[1:], op)
514 if not validate_args(op, args):
515 exit(1)
516
517 if not sys.argv[0].endswith('ifquery') and not os.geteuid() == 0:
518 print 'error: must be root to run this command'
519 exit(1)
520
521 if not sys.argv[0].endswith('ifquery') and not utils.lockFile(lockfile):
522 print 'Another instance of this program is already running.'
523 exit(0)
524
525 read_config(args)
526 init(args)
527 handlers.get(op)(args)
528 except Exception, e:
529 if not str(e):
530 exit(1)
531 if args and args.debug:
532 raise
533 else:
534 if logger:
535 logger.error(str(e))
536 else:
537 print str(e)
538 #if args and not args.debug:
539 # print '\nrerun the command with \'-d\' for a detailed errormsg'
540 exit(1)
541 finally:
542 deinit()
543
544 if __name__ == "__main__":
545
546 # required during boot
547 os.putenv('PATH', ENVPATH)
548 resource.setrlimit(resource.RLIMIT_CORE, (0,0))
549 main(sys.argv)