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