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