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