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