]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/ifupdown/iface.py
add new ifupdown2.conf option ifreload_down_changed to control ifreload
[mirror_ifupdown2.git] / ifupdown2 / ifupdown / iface.py
CommitLineData
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# iface --
7# interface object
8#
9
10"""ifupdown2 network interface object
11
12It is modeled based on the 'iface' section in /etc/network/interfaces
13file. But can be extended to include any other network interface format
14"""
15
16from collections import OrderedDict
17import logging
18import json
19
f82758bf
RP
20class ifaceType():
21 UNKNOWN = 0x0
22 IFACE = 0x1
23 BRIDGE_VLAN = 0x2
24
25class ifaceLinkKind():
26 UNKNOWN = 0x0
27 BRIDGE = 0x1
28 BOND = 0x2
29
30class ifaceLinkType():
31 LINK_UNKNOWN = 0x0
32 LINK_SLAVE = 0x1
33 LINK_MASTER = 0x2
34 LINK_NA = 0x3
35
faaa176e
RP
36class ifaceDependencyType():
37 """ Indicates type of dependency.
38
39 This class enumerates types of dependency relationships
40 between interfaces.
41
42 iface dependency relationships can be classified
43 into:
44 - link
45 - master/slave
46
47 In a 'link' dependency relationship, dependency can be shared
48 between interfaces. example: swp1.100 and
49 swp1.200 can both have 'link' swp1. swp1 is also a dependency
50 of swp1.100 and swp1.200. As you can see dependency
51 swp1 is shared between swp1.100 and swp1.200.
52
53 In a master/slave relationship like bridge and
54 its ports: eg: bridge br0 and its ports swp1 and swp2.
55 dependency swp1 and swp2 cannot be shared with any other
56 interface with the same dependency relationship.
57 ie, swp1 and swp2 cannot be in a slave relationship
58 with another interface. Understanding the dependency type is
59 required for any semantic checks between dependencies.
60
61 """
62 UNKNOWN = 0x0
63 LINK = 0x1
64 MASTER_SLAVE = 0x2
65
2c8c4ce7
RP
66class ifaceStatus():
67 """Enumerates iface status """
68
69 UNKNOWN = 0x1
70 SUCCESS = 0x2
71 ERROR = 0x3
72 NOTFOUND = 0x4
73
74 @classmethod
75 def to_str(cls, state):
76 if state == cls.UNKNOWN:
77 return 'unknown'
78 elif state == cls.SUCCESS:
79 return 'success'
80 elif state == cls.ERROR:
81 return 'error'
82 elif state == cls.NOTFOUND:
83 return 'notfound'
84
85 @classmethod
86 def from_str(cls, state_str):
87 if state_str == 'unknown':
88 return cls.UNKNOWN
89 elif state_str == 'success':
90 return cls.SUCCESS
91 elif state_str == 'error':
92 return cls.ERROR
93
94class ifaceState():
95 """Enumerates iface state """
96
97 UNKNOWN = 0x1
98 NEW = 0x2
99 PRE_UP = 0x3
100 UP = 0x4
101 POST_UP = 0x5
102 PRE_DOWN = 0x6
103 DOWN = 0x7
104 POST_DOWN = 0x8
105
106 # Pseudo states
107 QUERY_CHECKCURR = 0x9
108 QUERY_RUNNING = 0xa
109
110 @classmethod
111 def to_str(cls, state):
112 if state == cls.UNKNOWN:
113 return 'unknown'
114 elif state == cls.NEW:
115 return 'new'
116 elif state == cls.PRE_UP:
117 return 'pre-up'
118 elif state == cls.UP:
119 return 'up'
120 elif state == cls.POST_UP:
121 return 'post-up'
122 elif state == cls.PRE_DOWN:
123 return 'pre-down'
124 elif state == cls.DOWN:
125 return 'down'
126 elif state == cls.POST_DOWN:
127 return 'post-down'
128 elif state == cls.QUERY_CHECKCURR:
129 return 'query-checkcurr'
130 elif state == cls.QUERY_RUNNING:
131 return 'query-running'
132
133 @classmethod
134 def from_str(cls, state_str):
135 if state_str == 'unknown':
136 return cls.UNKNOWN
137 elif state_str == 'new':
138 return cls.NEW
139 elif state_str == 'pre-up':
140 return cls.PRE_UP
141 elif state_str == 'up':
142 return cls.UP
143 elif state_str == 'post-up':
144 return cls.POST_UP
145 elif state_str == 'pre-down':
146 return cls.PRE_DOWN
147 elif state_str == 'down':
148 return cls.DOWN
149 elif state_str == 'post-down':
150 return cls.POST_DOWN
151 elif state_str == 'query-checkcurr':
152 return cls.QUERY_CHECKCURR
153 elif state_str == 'query-running':
154 return cls.QUERY_RUNNING
155
156class ifaceJsonEncoder(json.JSONEncoder):
157 def default(self, o):
158 retconfig = {}
159 if o.config:
160 retconfig = dict((k, (v[0] if len(v) == 1 else v))
161 for k,v in o.config.items())
162 return OrderedDict({'name' : o.name,
163 'addr_method' : o.addr_method,
164 'addr_family' : o.addr_family,
165 'auto' : o.auto,
166 'config' : retconfig})
167
168class ifaceJsonDecoder():
169 @classmethod
170 def json_to_ifaceobj(cls, ifaceattrdict):
171 ifaceattrdict['config'] = OrderedDict([(k, (v if isinstance(v, list)
172 else [v]))
173 for k,v in ifaceattrdict.get('config',
174 OrderedDict()).items()])
175 return iface(attrsdict=ifaceattrdict)
176
177class iface():
178 """ ifupdown2 iface object class
179
180 Attributes:
181 **name** Name of the interface
182
183 **addr_family** Address family eg, inet, inet6. Can be None to
184 indicate both address families
185
186 **addr_method** Address method eg, static, manual or None for
187 static address method
188
189 **config** dictionary of config lines for this interface
190
191 **state** Configuration state of an interface as defined by
192 ifaceState
193
194 **status** Configuration status of an interface as defined by
195 ifaceStatus
196
197 **flags** Internal flags used by iface processing
198
199 **priv_flags** private flags owned by module using this class
200
f7e2962a
ST
201 **module_flags** module flags owned by module using this class
202
2c8c4ce7
RP
203 **refcnt** reference count, indicating number of interfaces
204 dependent on this iface
205
206 **lowerifaces** list of interface names lower to this interface or
207 this interface depends on
208
209 **upperifaces** list of interface names upper to this interface or
210 the interfaces that depend on this interface
211
212 **auto** True if interface belongs to the auto class
213
214 **classes** List of classes the interface belongs to
215
216 **env** shell environment the interface needs during
217 execution
218
219 **raw_config** raw interface config from file
220 """
221
222 # flag to indicate that the object was created from pickled state
f82758bf
RP
223 _PICKLED = 0x00000001
224 HAS_SIBLINGS = 0x00000010
225 IFACERANGE_ENTRY = 0x00000100
226 IFACERANGE_START = 0x00001000
2c8c4ce7
RP
227
228 version = '0.1'
229
230 def __init__(self, attrsdict={}):
231 self._set_attrs_from_dict(attrsdict)
232 self._config_status = {}
233 """dict with config status of iface attributes"""
234 self.state = ifaceState.NEW
235 """iface state (of type ifaceState) """
236 self.status = ifaceStatus.UNKNOWN
237 """iface status (of type ifaceStatus) """
f82758bf
RP
238 self.status_str = None
239 """iface status str (string representing the status) """
2c8c4ce7
RP
240 self.flags = 0x0
241 """iface flags """
242 self.priv_flags = 0x0
f7e2962a
ST
243 """iface module flags dictionary with module name: flags"""
244 self.module_flags = {}
2c8c4ce7
RP
245 """iface priv flags. can be used by the external object manager """
246 self.refcnt = 0
247 """iface refcnt (incremented for each dependent this interface has) """
f82758bf 248 self.lowerifaces = None
2c8c4ce7
RP
249 """lower iface list (in other words: slaves of this interface """
250 self.upperifaces = None
251 """upper iface list (in other words: master of this interface """
252 self.classes = []
253 """interface classes this iface belongs to """
254 self.env = None
255 """environment variable dict required for this interface to run"""
256 self.raw_config = []
257 """interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
258 self.linkstate = None
259 """linkstate of the interface"""
f82758bf
RP
260 self.type = ifaceType.UNKNOWN
261 """interface type"""
262 self.priv_data = None
263 self.realname = None
264 self.link_type = ifaceLinkType.LINK_UNKNOWN
265 self.link_kind = ifaceLinkKind.UNKNOWN
2c8c4ce7 266
faaa176e
RP
267 # The below attribute is used to disambiguate between various
268 # types of dependencies
269 self.dependency_type = ifaceDependencyType.UNKNOWN
270
2c8c4ce7
RP
271 def _set_attrs_from_dict(self, attrdict):
272 self.auto = attrdict.get('auto', False)
273 self.name = attrdict.get('name')
274 self.addr_family = attrdict.get('addr_family')
275 self.addr_method = attrdict.get('addr_method')
276 self.config = attrdict.get('config', OrderedDict())
277
278 def inc_refcnt(self):
279 """ increment refcnt of the interface. Usually used to indicate that
280 it has dependents """
281 self.refcnt += 1
282
283 def dec_refcnt(self):
284 """ decrement refcnt of the interface. Usually used to indicate that
285 it has lost its dependent """
286 self.refcnt -= 1
287
288 def is_config_present(self):
289 """ returns true if the interface has user provided config,
290 false otherwise """
291 addr_method = self.addr_method
292 if addr_method and addr_method in ['dhcp', 'dhcp6', 'loopback']:
293 return True
294 if not self.config:
295 return False
296 else:
297 return True
298
299 def set_class(self, classname):
300 """ appends class to the interfaces class list """
301 self.classes.append(classname)
302
303 def set_state_n_status(self, state, status):
304 """ sets state and status of an interface """
305 self.state = state
306 self.status = status
307
308 def set_flag(self, flag):
309 self.flags |= flag
310
311 def clear_flag(self, flag):
312 self.flags &= ~flag
313
314 def add_to_upperifaces(self, upperifacename):
315 """ add to the list of upperifaces """
316 if self.upperifaces:
317 if upperifacename not in self.upperifaces:
318 self.upperifaces.append(upperifacename)
319 else:
320 self.upperifaces = [upperifacename]
321
322 def get_attr_value(self, attr_name):
323 """ add to the list of upperifaces """
324 return self.config.get(attr_name)
325
326 def get_attr_value_first(self, attr_name):
327 """ get first value of the specified attr name """
328 attr_value_list = self.config.get(attr_name)
329 if attr_value_list:
330 return attr_value_list[0]
331 return None
332
f82758bf
RP
333 def get_attrs_value_first(self, attrs):
334 """ get first value of the first attr in the list.
335 Useful when you have multiple attrs representing the
336 same thing.
337 """
338 for attr in attrs:
339 attr_value_list = self.config.get(attr)
340 if attr_value_list:
341 return attr_value_list[0]
342 return None
343
2c8c4ce7
RP
344 def get_attr_value_n(self, attr_name, attr_index):
345 """ get n'th value of the specified attr name """
346 attr_value_list = self.config.get(attr_name)
347 if attr_value_list:
348 try:
349 return attr_value_list[attr_index]
350 except:
351 return None
352 return None
353
354 @property
355 def get_env(self):
356 """ get shell environment variables the interface must execute in """
357 if not self.env:
358 self.generate_env()
359 return self.env
360
361 def generate_env(self):
362 """ generate shell environment variables dict interface must execute
363 in. This is used to support legacy ifupdown scripts
364 """
365 env = {}
366 config = self.config
367 env['IFACE'] = self.name
368 for attr, attr_value in config.items():
369 attr_env_name = 'IF_%s' %attr.upper()
370 env[attr_env_name] = attr_value[0]
371 if env:
372 self.env = env
373
374 def update_config(self, attr_name, attr_value):
375 """ add attribute name and value to the interface config """
376 self.config.setdefault(attr_name, []).append(attr_value)
377
f82758bf
RP
378 def replace_config(self, attr_name, attr_value):
379 """ add attribute name and value to the interface config """
380 self.config[attr_name] = [attr_value]
381
382 def delete_config(self, attr_name):
383 """ add attribute name and value to the interface config """
384 try:
385 del self.config[attr_name]
386 except:
387 pass
388
2c8c4ce7
RP
389 def update_config_dict(self, attrdict):
390 self.config.update(attrdict)
391
392 def update_config_with_status(self, attr_name, attr_value, attr_status=0):
393 """ add attribute name and value to the interface config and also
394 update the config_status dict with status of this attribute config """
395 if not attr_value:
396 attr_value = ''
397 self.config.setdefault(attr_name, []).append(attr_value)
398 self._config_status.setdefault(attr_name, []).append(attr_status)
2c8c4ce7 399 # set global iface state
f82758bf 400 if attr_status == 1:
2c8c4ce7
RP
401 self.status = ifaceStatus.ERROR
402 elif self.status != ifaceStatus.ERROR:
403 # Not already error, mark success
404 self.status = ifaceStatus.SUCCESS
405
f82758bf
RP
406 def check_n_update_config_with_status_many(self, ifaceobjorig, attr_names,
407 attr_status=0):
408 # set multiple attribute status to zero
409 # also updates status only if the attribute is present
410 for attr_name in attr_names:
411 if not ifaceobjorig.get_attr_value_first(attr_name):
412 continue
413 self.config.setdefault(attr_name, []).append('')
414 self._config_status.setdefault(attr_name, []).append(attr_status)
415
2c8c4ce7
RP
416 def get_config_attr_status(self, attr_name, idx=0):
417 """ get status of a attribute config on this interface.
418
419 Looks at the iface _config_status dict"""
420 return self._config_status.get(attr_name, [])[idx]
421
422 def compare(self, dstiface):
423 """ compares iface object with iface object passed as argument
424
425 Returns True if object self is same as dstiface and False otherwise """
426
427 if self.name != dstiface.name: return False
f82758bf 428 if self.type != dstiface.type: return False
2c8c4ce7
RP
429 if self.addr_family != dstiface.addr_family: return False
430 if self.addr_method != dstiface.addr_method: return False
431 if self.auto != dstiface.auto: return False
432 if self.classes != dstiface.classes: return False
f8e2f6e2
RP
433 if len(self.config) != len(dstiface.config):
434 return False
2c8c4ce7
RP
435 if any(True for k in self.config if k not in dstiface.config):
436 return False
437 if any(True for k,v in self.config.items()
438 if v != dstiface.config.get(k)): return False
439 return True
440
f82758bf 441
2c8c4ce7
RP
442 def __getstate__(self):
443 odict = self.__dict__.copy()
444 del odict['state']
445 del odict['status']
446 del odict['lowerifaces']
447 del odict['upperifaces']
448 del odict['refcnt']
449 del odict['_config_status']
450 del odict['flags']
451 del odict['priv_flags']
f7e2962a 452 del odict['module_flags']
2c8c4ce7
RP
453 del odict['raw_config']
454 del odict['linkstate']
455 del odict['env']
f82758bf
RP
456 del odict['link_type']
457 del odict['link_kind']
faaa176e 458 del odict['dependency_type']
2c8c4ce7
RP
459 return odict
460
461 def __setstate__(self, dict):
462 self.__dict__.update(dict)
463 self._config_status = {}
464 self.state = ifaceState.NEW
465 self.status = ifaceStatus.UNKNOWN
466 self.refcnt = 0
467 self.flags = 0
468 self.lowerifaces = None
469 self.upperifaces = None
470 self.linkstate = None
471 self.env = None
472 self.priv_flags = 0
f7e2962a 473 self.module_flags = {}
2c8c4ce7
RP
474 self.raw_config = []
475 self.flags |= self._PICKLED
f82758bf
RP
476 self.link_type = ifaceLinkType.LINK_NA
477 self.link_kind = ifaceLinkKind.UNKNOWN
faaa176e 478 self.dependency_type = ifaceDependencyType.UNKNOWN
2c8c4ce7
RP
479
480 def dump_raw(self, logger):
481 indent = ' '
482 if self.auto:
483 print 'auto %s' %self.name
484 print (self.raw_config[0])
485 for i in range(1, len(self.raw_config)):
486 print(indent + self.raw_config[i])
487
488 def dump(self, logger):
489 indent = '\t'
490 logger.info(self.name + ' : {')
491 logger.info(indent + 'family: %s' %self.addr_family)
492 logger.info(indent + 'method: %s' %self.addr_method)
493 logger.info(indent + 'flags: %x' %self.flags)
494 logger.info(indent + 'state: %s'
495 %ifaceState.to_str(self.state))
496 logger.info(indent + 'status: %s'
497 %ifaceStatus.to_str(self.status))
498 logger.info(indent + 'refcnt: %d' %self.refcnt)
499 d = self.lowerifaces
500 if d:
501 logger.info(indent + 'lowerdevs: %s' %str(d))
502 else:
503 logger.info(indent + 'lowerdevs: None')
504
505 logger.info(indent + 'config: ')
506 config = self.config
507 if config:
508 logger.info(indent + indent + str(config))
509 logger.info('}')
510
511 def dump_pretty(self, with_status=False,
f82758bf
RP
512 successstr='success', errorstr='error',
513 unknownstr='unknown', use_realname=False):
2c8c4ce7
RP
514 indent = '\t'
515 outbuf = ''
f82758bf
RP
516 if use_realname and self.realname:
517 name = '%s' %self.realname
518 else:
519 name = '%s' %self.name
2c8c4ce7 520 if self.auto:
f82758bf
RP
521 outbuf += 'auto %s\n' %name
522 ifaceline = ''
523 if self.type == ifaceType.BRIDGE_VLAN:
524 ifaceline += 'vlan %s' %name
525 else:
526 ifaceline += 'iface %s' %name
2c8c4ce7 527 if self.addr_family:
f82758bf 528 ifaceline += ' %s' %self.addr_family
2c8c4ce7 529 if self.addr_method:
f82758bf 530 ifaceline += ' %s' %self.addr_method
2c8c4ce7 531 if with_status:
f82758bf
RP
532 status_str = None
533 if (self.status == ifaceStatus.ERROR or
534 self.status == ifaceStatus.NOTFOUND):
535 if self.status_str:
536 ifaceline += ' (%s)' %self.status_str
537 status_str = errorstr
2c8c4ce7 538 elif self.status == ifaceStatus.SUCCESS:
f82758bf
RP
539 status_str = successstr
540 if status_str:
541 outbuf += '{0:65} {1:>8}'.format(ifaceline, status_str) + '\n'
542 else:
543 outbuf += ifaceline + '\n'
2c8c4ce7 544 if self.status == ifaceStatus.NOTFOUND:
f82758bf
RP
545 outbuf = (outbuf.encode('utf8')
546 if isinstance(outbuf, unicode) else outbuf)
2c8c4ce7
RP
547 print outbuf + '\n'
548 return
f82758bf
RP
549 else:
550 outbuf += ifaceline + '\n'
2c8c4ce7
RP
551 config = self.config
552 if config:
553 for cname, cvaluelist in config.items():
554 idx = 0
555 for cv in cvaluelist:
2c8c4ce7
RP
556 if with_status:
557 s = self.get_config_attr_status(cname, idx)
f82758bf
RP
558 if s == -1:
559 status_str = unknownstr
560 elif s == 1:
561 status_str = errorstr
2c8c4ce7 562 elif s == 0:
f82758bf
RP
563 status_str = successstr
564 outbuf += (indent + '{0:55} {1:>10}'.format(
565 '%s %s' %(cname, cv), status_str)) + '\n'
2c8c4ce7
RP
566 else:
567 outbuf += indent + '%s %s\n' %(cname, cv)
568 idx += 1
569 if with_status:
570 outbuf = (outbuf.encode('utf8')
571 if isinstance(outbuf, unicode) else outbuf)
572 print outbuf