]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/ifupdown/iface.py
Add config len check to iface compare
[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
2c8c4ce7
RP
36class 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
64class 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
126class 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
138class 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
147class 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 **refcnt** reference count, indicating number of interfaces
172 dependent on this iface
173
174 **lowerifaces** list of interface names lower to this interface or
175 this interface depends on
176
177 **upperifaces** list of interface names upper to this interface or
178 the interfaces that depend on this interface
179
180 **auto** True if interface belongs to the auto class
181
182 **classes** List of classes the interface belongs to
183
184 **env** shell environment the interface needs during
185 execution
186
187 **raw_config** raw interface config from file
188 """
189
190 # flag to indicate that the object was created from pickled state
f82758bf
RP
191 _PICKLED = 0x00000001
192 HAS_SIBLINGS = 0x00000010
193 IFACERANGE_ENTRY = 0x00000100
194 IFACERANGE_START = 0x00001000
2c8c4ce7
RP
195
196 version = '0.1'
197
198 def __init__(self, attrsdict={}):
199 self._set_attrs_from_dict(attrsdict)
200 self._config_status = {}
201 """dict with config status of iface attributes"""
202 self.state = ifaceState.NEW
203 """iface state (of type ifaceState) """
204 self.status = ifaceStatus.UNKNOWN
205 """iface status (of type ifaceStatus) """
f82758bf
RP
206 self.status_str = None
207 """iface status str (string representing the status) """
2c8c4ce7
RP
208 self.flags = 0x0
209 """iface flags """
210 self.priv_flags = 0x0
211 """iface priv flags. can be used by the external object manager """
212 self.refcnt = 0
213 """iface refcnt (incremented for each dependent this interface has) """
f82758bf 214 self.lowerifaces = None
2c8c4ce7
RP
215 """lower iface list (in other words: slaves of this interface """
216 self.upperifaces = None
217 """upper iface list (in other words: master of this interface """
218 self.classes = []
219 """interface classes this iface belongs to """
220 self.env = None
221 """environment variable dict required for this interface to run"""
222 self.raw_config = []
223 """interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
224 self.linkstate = None
225 """linkstate of the interface"""
f82758bf
RP
226 self.type = ifaceType.UNKNOWN
227 """interface type"""
228 self.priv_data = None
229 self.realname = None
230 self.link_type = ifaceLinkType.LINK_UNKNOWN
231 self.link_kind = ifaceLinkKind.UNKNOWN
2c8c4ce7
RP
232
233 def _set_attrs_from_dict(self, attrdict):
234 self.auto = attrdict.get('auto', False)
235 self.name = attrdict.get('name')
236 self.addr_family = attrdict.get('addr_family')
237 self.addr_method = attrdict.get('addr_method')
238 self.config = attrdict.get('config', OrderedDict())
239
240 def inc_refcnt(self):
241 """ increment refcnt of the interface. Usually used to indicate that
242 it has dependents """
243 self.refcnt += 1
244
245 def dec_refcnt(self):
246 """ decrement refcnt of the interface. Usually used to indicate that
247 it has lost its dependent """
248 self.refcnt -= 1
249
250 def is_config_present(self):
251 """ returns true if the interface has user provided config,
252 false otherwise """
253 addr_method = self.addr_method
254 if addr_method and addr_method in ['dhcp', 'dhcp6', 'loopback']:
255 return True
256 if not self.config:
257 return False
258 else:
259 return True
260
261 def set_class(self, classname):
262 """ appends class to the interfaces class list """
263 self.classes.append(classname)
264
265 def set_state_n_status(self, state, status):
266 """ sets state and status of an interface """
267 self.state = state
268 self.status = status
269
270 def set_flag(self, flag):
271 self.flags |= flag
272
273 def clear_flag(self, flag):
274 self.flags &= ~flag
275
276 def add_to_upperifaces(self, upperifacename):
277 """ add to the list of upperifaces """
278 if self.upperifaces:
279 if upperifacename not in self.upperifaces:
280 self.upperifaces.append(upperifacename)
281 else:
282 self.upperifaces = [upperifacename]
283
284 def get_attr_value(self, attr_name):
285 """ add to the list of upperifaces """
286 return self.config.get(attr_name)
287
288 def get_attr_value_first(self, attr_name):
289 """ get first value of the specified attr name """
290 attr_value_list = self.config.get(attr_name)
291 if attr_value_list:
292 return attr_value_list[0]
293 return None
294
f82758bf
RP
295 def get_attrs_value_first(self, attrs):
296 """ get first value of the first attr in the list.
297 Useful when you have multiple attrs representing the
298 same thing.
299 """
300 for attr in attrs:
301 attr_value_list = self.config.get(attr)
302 if attr_value_list:
303 return attr_value_list[0]
304 return None
305
2c8c4ce7
RP
306 def get_attr_value_n(self, attr_name, attr_index):
307 """ get n'th value of the specified attr name """
308 attr_value_list = self.config.get(attr_name)
309 if attr_value_list:
310 try:
311 return attr_value_list[attr_index]
312 except:
313 return None
314 return None
315
316 @property
317 def get_env(self):
318 """ get shell environment variables the interface must execute in """
319 if not self.env:
320 self.generate_env()
321 return self.env
322
323 def generate_env(self):
324 """ generate shell environment variables dict interface must execute
325 in. This is used to support legacy ifupdown scripts
326 """
327 env = {}
328 config = self.config
329 env['IFACE'] = self.name
330 for attr, attr_value in config.items():
331 attr_env_name = 'IF_%s' %attr.upper()
332 env[attr_env_name] = attr_value[0]
333 if env:
334 self.env = env
335
336 def update_config(self, attr_name, attr_value):
337 """ add attribute name and value to the interface config """
338 self.config.setdefault(attr_name, []).append(attr_value)
339
f82758bf
RP
340 def replace_config(self, attr_name, attr_value):
341 """ add attribute name and value to the interface config """
342 self.config[attr_name] = [attr_value]
343
344 def delete_config(self, attr_name):
345 """ add attribute name and value to the interface config """
346 try:
347 del self.config[attr_name]
348 except:
349 pass
350
2c8c4ce7
RP
351 def update_config_dict(self, attrdict):
352 self.config.update(attrdict)
353
354 def update_config_with_status(self, attr_name, attr_value, attr_status=0):
355 """ add attribute name and value to the interface config and also
356 update the config_status dict with status of this attribute config """
357 if not attr_value:
358 attr_value = ''
359 self.config.setdefault(attr_name, []).append(attr_value)
360 self._config_status.setdefault(attr_name, []).append(attr_status)
2c8c4ce7 361 # set global iface state
f82758bf 362 if attr_status == 1:
2c8c4ce7
RP
363 self.status = ifaceStatus.ERROR
364 elif self.status != ifaceStatus.ERROR:
365 # Not already error, mark success
366 self.status = ifaceStatus.SUCCESS
367
f82758bf
RP
368 def check_n_update_config_with_status_many(self, ifaceobjorig, attr_names,
369 attr_status=0):
370 # set multiple attribute status to zero
371 # also updates status only if the attribute is present
372 for attr_name in attr_names:
373 if not ifaceobjorig.get_attr_value_first(attr_name):
374 continue
375 self.config.setdefault(attr_name, []).append('')
376 self._config_status.setdefault(attr_name, []).append(attr_status)
377
2c8c4ce7
RP
378 def get_config_attr_status(self, attr_name, idx=0):
379 """ get status of a attribute config on this interface.
380
381 Looks at the iface _config_status dict"""
382 return self._config_status.get(attr_name, [])[idx]
383
384 def compare(self, dstiface):
385 """ compares iface object with iface object passed as argument
386
387 Returns True if object self is same as dstiface and False otherwise """
388
389 if self.name != dstiface.name: return False
f82758bf 390 if self.type != dstiface.type: return False
2c8c4ce7
RP
391 if self.addr_family != dstiface.addr_family: return False
392 if self.addr_method != dstiface.addr_method: return False
393 if self.auto != dstiface.auto: return False
394 if self.classes != dstiface.classes: return False
f8e2f6e2
RP
395 if len(self.config) != len(dstiface.config):
396 return False
2c8c4ce7
RP
397 if any(True for k in self.config if k not in dstiface.config):
398 return False
399 if any(True for k,v in self.config.items()
400 if v != dstiface.config.get(k)): return False
401 return True
402
f82758bf 403
2c8c4ce7
RP
404 def __getstate__(self):
405 odict = self.__dict__.copy()
406 del odict['state']
407 del odict['status']
408 del odict['lowerifaces']
409 del odict['upperifaces']
410 del odict['refcnt']
411 del odict['_config_status']
412 del odict['flags']
413 del odict['priv_flags']
414 del odict['raw_config']
415 del odict['linkstate']
416 del odict['env']
f82758bf
RP
417 del odict['link_type']
418 del odict['link_kind']
2c8c4ce7
RP
419 return odict
420
421 def __setstate__(self, dict):
422 self.__dict__.update(dict)
423 self._config_status = {}
424 self.state = ifaceState.NEW
425 self.status = ifaceStatus.UNKNOWN
426 self.refcnt = 0
427 self.flags = 0
428 self.lowerifaces = None
429 self.upperifaces = None
430 self.linkstate = None
431 self.env = None
432 self.priv_flags = 0
433 self.raw_config = []
434 self.flags |= self._PICKLED
f82758bf
RP
435 self.link_type = ifaceLinkType.LINK_NA
436 self.link_kind = ifaceLinkKind.UNKNOWN
2c8c4ce7
RP
437
438 def dump_raw(self, logger):
439 indent = ' '
440 if self.auto:
441 print 'auto %s' %self.name
442 print (self.raw_config[0])
443 for i in range(1, len(self.raw_config)):
444 print(indent + self.raw_config[i])
445
446 def dump(self, logger):
447 indent = '\t'
448 logger.info(self.name + ' : {')
449 logger.info(indent + 'family: %s' %self.addr_family)
450 logger.info(indent + 'method: %s' %self.addr_method)
451 logger.info(indent + 'flags: %x' %self.flags)
452 logger.info(indent + 'state: %s'
453 %ifaceState.to_str(self.state))
454 logger.info(indent + 'status: %s'
455 %ifaceStatus.to_str(self.status))
456 logger.info(indent + 'refcnt: %d' %self.refcnt)
457 d = self.lowerifaces
458 if d:
459 logger.info(indent + 'lowerdevs: %s' %str(d))
460 else:
461 logger.info(indent + 'lowerdevs: None')
462
463 logger.info(indent + 'config: ')
464 config = self.config
465 if config:
466 logger.info(indent + indent + str(config))
467 logger.info('}')
468
469 def dump_pretty(self, with_status=False,
f82758bf
RP
470 successstr='success', errorstr='error',
471 unknownstr='unknown', use_realname=False):
2c8c4ce7
RP
472 indent = '\t'
473 outbuf = ''
f82758bf
RP
474 if use_realname and self.realname:
475 name = '%s' %self.realname
476 else:
477 name = '%s' %self.name
2c8c4ce7 478 if self.auto:
f82758bf
RP
479 outbuf += 'auto %s\n' %name
480 ifaceline = ''
481 if self.type == ifaceType.BRIDGE_VLAN:
482 ifaceline += 'vlan %s' %name
483 else:
484 ifaceline += 'iface %s' %name
2c8c4ce7 485 if self.addr_family:
f82758bf 486 ifaceline += ' %s' %self.addr_family
2c8c4ce7 487 if self.addr_method:
f82758bf 488 ifaceline += ' %s' %self.addr_method
2c8c4ce7 489 if with_status:
f82758bf
RP
490 status_str = None
491 if (self.status == ifaceStatus.ERROR or
492 self.status == ifaceStatus.NOTFOUND):
493 if self.status_str:
494 ifaceline += ' (%s)' %self.status_str
495 status_str = errorstr
2c8c4ce7 496 elif self.status == ifaceStatus.SUCCESS:
f82758bf
RP
497 status_str = successstr
498 if status_str:
499 outbuf += '{0:65} {1:>8}'.format(ifaceline, status_str) + '\n'
500 else:
501 outbuf += ifaceline + '\n'
2c8c4ce7 502 if self.status == ifaceStatus.NOTFOUND:
f82758bf
RP
503 outbuf = (outbuf.encode('utf8')
504 if isinstance(outbuf, unicode) else outbuf)
2c8c4ce7
RP
505 print outbuf + '\n'
506 return
f82758bf
RP
507 else:
508 outbuf += ifaceline + '\n'
2c8c4ce7
RP
509 config = self.config
510 if config:
511 for cname, cvaluelist in config.items():
512 idx = 0
513 for cv in cvaluelist:
2c8c4ce7
RP
514 if with_status:
515 s = self.get_config_attr_status(cname, idx)
f82758bf
RP
516 if s == -1:
517 status_str = unknownstr
518 elif s == 1:
519 status_str = errorstr
2c8c4ce7 520 elif s == 0:
f82758bf
RP
521 status_str = successstr
522 outbuf += (indent + '{0:55} {1:>10}'.format(
523 '%s %s' %(cname, cv), status_str)) + '\n'
2c8c4ce7
RP
524 else:
525 outbuf += indent + '%s %s\n' %(cname, cv)
526 idx += 1
527 if with_status:
528 outbuf = (outbuf.encode('utf8')
529 if isinstance(outbuf, unicode) else outbuf)
530 print outbuf