]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown/iface.py
move link_set 'up' errors to debug
[mirror_ifupdown2.git] / ifupdown / iface.py
CommitLineData
a6f80f0e 1#!/usr/bin/python
3e8ee54f 2#
904908bc 3# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
3e8ee54f 4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6# iface --
7# interface object
8#
62ddec8b 9
10"""ifupdown2 network interface object
11
904908bc 12It is modeled based on the 'iface' section in /etc/network/interfaces
62ddec8b 13file. But can be extended to include any other network interface format
62ddec8b 14"""
15
cca03c30 16from collections import OrderedDict
d08d5f54 17import logging
2cd06f78 18import json
a6f80f0e 19
41febf89 20class ifaceType():
2da58137
RP
21 UNKNOWN = 0x0
22 IFACE = 0x1
23 BRIDGE_VLAN = 0x2
41febf89 24
a9ab1b4f
RP
25class ifaceLinkKind():
26 UNKNOWN = 0x0
27 BRIDGE = 0x1
28 BOND = 0x2
29
c416da6a 30class ifaceLinkType():
a070c90e 31 LINK_UNKNOWN = 0x0
c416da6a
RP
32 LINK_SLAVE = 0x1
33 LINK_MASTER = 0x2
a070c90e 34 LINK_NA = 0x3
c416da6a 35
a6f80f0e 36class ifaceStatus():
62ddec8b 37 """Enumerates iface status """
38
a6f80f0e 39 UNKNOWN = 0x1
40 SUCCESS = 0x2
41 ERROR = 0x3
10720a53 42 NOTFOUND = 0x4
a6f80f0e 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'
739f665b 52 elif state == cls.NOTFOUND:
69f58278 53 return 'notfound'
a6f80f0e 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():
62ddec8b 65 """Enumerates iface state """
a6f80f0e 66
a6f80f0e 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
d08d5f54 76 # Pseudo states
77 QUERY_CHECKCURR = 0x9
78 QUERY_RUNNING = 0xa
79
a6f80f0e 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'
d08d5f54 94 elif state == cls.DOWN:
95 return 'down'
a6f80f0e 96 elif state == cls.POST_DOWN:
97 return 'post-down'
d08d5f54 98 elif state == cls.QUERY_CHECKCURR:
99 return 'query-checkcurr'
100 elif state == cls.QUERY_RUNNING:
101 return 'query-running'
a6f80f0e 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
d08d5f54 117 elif state_str == 'down':
118 return cls.DOWN
a6f80f0e 119 elif state_str == 'post-down':
120 return cls.POST_DOWN
d08d5f54 121 elif state_str == 'query-checkcurr':
122 return cls.QUERY_CHECKCURR
123 elif state_str == 'query-running':
124 return cls.QUERY_RUNNING
a6f80f0e 125
d08d5f54 126class ifaceJsonEncoder(json.JSONEncoder):
127 def default(self, o):
83c1f241 128 retconfig = {}
129 if o.config:
3dcc1d0e 130 retconfig = dict((k, (v[0] if len(v) == 1 else v))
131 for k,v in o.config.items())
75730152 132 return OrderedDict({'name' : o.name,
133 'addr_method' : o.addr_method,
134 'addr_family' : o.addr_family,
135 'auto' : o.auto,
83c1f241 136 'config' : retconfig})
d08d5f54 137
3dcc1d0e 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
a6f80f0e 147class iface():
904908bc 148 """ ifupdown2 iface object class
62ddec8b 149
150 Attributes:
904908bc
RP
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
62ddec8b 188 """
189
31a5f4c3 190 # flag to indicate that the object was created from pickled state
73c27592
RP
191 _PICKLED = 0x00000001
192 HAS_SIBLINGS = 0x00000010
193 IFACERANGE_ENTRY = 0x00000100
194 IFACERANGE_START = 0x00001000
a6f80f0e 195
37c0543d 196 version = '0.1'
197
3dcc1d0e 198 def __init__(self, attrsdict={}):
199 self._set_attrs_from_dict(attrsdict)
62ddec8b 200 self._config_status = {}
2c0ad8b3 201 """dict with config status of iface attributes"""
a6f80f0e 202 self.state = ifaceState.NEW
2c0ad8b3 203 """iface state (of type ifaceState) """
a6f80f0e 204 self.status = ifaceStatus.UNKNOWN
2c0ad8b3 205 """iface status (of type ifaceStatus) """
e1601369
RP
206 self.status_str = None
207 """iface status str (string representing the status) """
a6f80f0e 208 self.flags = 0x0
2c0ad8b3 209 """iface flags """
37c0543d 210 self.priv_flags = 0x0
2c0ad8b3 211 """iface priv flags. can be used by the external object manager """
a6f80f0e 212 self.refcnt = 0
2c0ad8b3 213 """iface refcnt (incremented for each dependent this interface has) """
41febf89 214 self.lowerifaces = None
2c0ad8b3 215 """lower iface list (in other words: slaves of this interface """
f3215127 216 self.upperifaces = None
2c0ad8b3 217 """upper iface list (in other words: master of this interface """
a6f80f0e 218 self.classes = []
2c0ad8b3 219 """interface classes this iface belongs to """
a6f80f0e 220 self.env = None
2c0ad8b3 221 """environment variable dict required for this interface to run"""
62ddec8b 222 self.raw_config = []
2c0ad8b3 223 """interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
a6f80f0e 224 self.linkstate = None
2c0ad8b3 225 """linkstate of the interface"""
41febf89
RP
226 self.type = ifaceType.UNKNOWN
227 """interface type"""
228 self.priv_data = None
2da58137 229 self.realname = None
a070c90e 230 self.link_type = ifaceLinkType.LINK_UNKNOWN
a9ab1b4f 231 self.link_kind = ifaceLinkKind.BRIDGE
a6f80f0e 232
3dcc1d0e 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
a6f80f0e 240 def inc_refcnt(self):
2c0ad8b3
RP
241 """ increment refcnt of the interface. Usually used to indicate that
242 it has dependents """
a6f80f0e 243 self.refcnt += 1
244
245 def dec_refcnt(self):
2c0ad8b3
RP
246 """ decrement refcnt of the interface. Usually used to indicate that
247 it has lost its dependent """
a6f80f0e 248 self.refcnt -= 1
249
cca03c30 250 def is_config_present(self):
2c0ad8b3
RP
251 """ returns true if the interface has user provided config,
252 false otherwise """
62ddec8b 253 addr_method = self.addr_method
2cd06f78 254 if addr_method and addr_method in ['dhcp', 'dhcp6', 'loopback']:
255 return True
fe0a57d3 256 if not self.config:
cca03c30 257 return False
fe0a57d3 258 else:
259 return True
cca03c30 260
3e8ee54f 261 def set_class(self, classname):
2c0ad8b3 262 """ appends class to the interfaces class list """
a6f80f0e 263 self.classes.append(classname)
264
31a5f4c3 265 def set_state_n_status(self, state, status):
2c0ad8b3 266 """ sets state and status of an interface """
31a5f4c3 267 self.state = state
268 self.status = status
269
a6f80f0e 270 def set_flag(self, flag):
271 self.flags |= flag
272
273 def clear_flag(self, flag):
274 self.flags &= ~flag
275
f3215127 276 def add_to_upperifaces(self, upperifacename):
2c0ad8b3 277 """ add to the list of upperifaces """
f3215127 278 if self.upperifaces:
279 if upperifacename not in self.upperifaces:
280 self.upperifaces.append(upperifacename)
281 else:
282 self.upperifaces = [upperifacename]
283
a6f80f0e 284 def get_attr_value(self, attr_name):
2c0ad8b3 285 """ add to the list of upperifaces """
62ddec8b 286 return self.config.get(attr_name)
a6f80f0e 287
288 def get_attr_value_first(self, attr_name):
2c0ad8b3 289 """ get first value of the specified attr name """
62ddec8b 290 attr_value_list = self.config.get(attr_name)
fe0a57d3 291 if attr_value_list:
a6f80f0e 292 return attr_value_list[0]
a6f80f0e 293 return None
294
679e6567
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
a6f80f0e 306 def get_attr_value_n(self, attr_name, attr_index):
2c0ad8b3 307 """ get n'th value of the specified attr name """
62ddec8b 308 attr_value_list = self.config.get(attr_name)
fe0a57d3 309 if attr_value_list:
a6f80f0e 310 try:
311 return attr_value_list[attr_index]
312 except:
313 return None
a6f80f0e 314 return None
315
62ddec8b 316 @property
a6f80f0e 317 def get_env(self):
2c0ad8b3 318 """ get shell environment variables the interface must execute in """
fe0a57d3 319 if not self.env:
a6f80f0e 320 self.generate_env()
321 return self.env
322
a6f80f0e 323 def generate_env(self):
2c0ad8b3
RP
324 """ generate shell environment variables dict interface must execute
325 in. This is used to support legacy ifupdown scripts
326 """
a6f80f0e 327 env = {}
62ddec8b 328 config = self.config
329 env['IFACE'] = self.name
a6f80f0e 330 for attr, attr_value in config.items():
331 attr_env_name = 'IF_%s' %attr.upper()
332 env[attr_env_name] = attr_value[0]
fe0a57d3 333 if env:
62ddec8b 334 self.env = env
a6f80f0e 335
739f665b 336 def update_config(self, attr_name, attr_value):
2c0ad8b3 337 """ add attribute name and value to the interface config """
62ddec8b 338 self.config.setdefault(attr_name, []).append(attr_value)
739f665b 339
d8e3554d
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
739f665b 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):
2c0ad8b3
RP
355 """ add attribute name and value to the interface config and also
356 update the config_status dict with status of this attribute config """
69f58278 357 if not attr_value:
a6f80f0e 358 attr_value = ''
62ddec8b 359 self.config.setdefault(attr_name, []).append(attr_value)
360 self._config_status.setdefault(attr_name, []).append(attr_status)
69f58278 361 # set global iface state
e1601369 362 if attr_status == 1:
62ddec8b 363 self.status = ifaceStatus.ERROR
364 elif self.status != ifaceStatus.ERROR:
69f58278 365 # Not already error, mark success
62ddec8b 366 self.status = ifaceStatus.SUCCESS
69f58278 367
a070c90e 368 def check_n_update_config_with_status_many(self, ifaceobjorig, attr_names,
e1601369
RP
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:
a070c90e
RP
373 if not ifaceobjorig.get_attr_value_first(attr_name):
374 continue
e1601369
RP
375 self.config.setdefault(attr_name, []).append('')
376 self._config_status.setdefault(attr_name, []).append(attr_status)
377
69f58278 378 def get_config_attr_status(self, attr_name, idx=0):
2c0ad8b3
RP
379 """ get status of a attribute config on this interface.
380
381 Looks at the iface _config_status dict"""
62ddec8b 382 return self._config_status.get(attr_name, [])[idx]
69f58278 383
ca3f4fc7 384 def compare(self, dstiface):
2c0ad8b3 385 """ compares iface object with iface object passed as argument
ca3f4fc7 386
387 Returns True if object self is same as dstiface and False otherwise """
388
389 if self.name != dstiface.name: return False
84ca006f 390 if self.type != dstiface.type: return False
ca3f4fc7 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
37c0543d 395 if any(True for k in self.config if k not in dstiface.config):
ca3f4fc7 396 return False
37c0543d 397 if any(True for k,v in self.config.items()
ca3f4fc7 398 if v != dstiface.config.get(k)): return False
399 return True
d08d5f54 400
e1601369 401
d08d5f54 402 def __getstate__(self):
403 odict = self.__dict__.copy()
404 del odict['state']
405 del odict['status']
f3215127 406 del odict['lowerifaces']
c798b0f4 407 del odict['upperifaces']
d08d5f54 408 del odict['refcnt']
62ddec8b 409 del odict['_config_status']
5c721925 410 del odict['flags']
411 del odict['priv_flags']
62ddec8b 412 del odict['raw_config']
5c721925 413 del odict['linkstate']
414 del odict['env']
c416da6a 415 del odict['link_type']
a9ab1b4f 416 del odict['link_kind']
d08d5f54 417 return odict
418
419 def __setstate__(self, dict):
420 self.__dict__.update(dict)
62ddec8b 421 self._config_status = {}
d08d5f54 422 self.state = ifaceState.NEW
423 self.status = ifaceStatus.UNKNOWN
424 self.refcnt = 0
5c721925 425 self.flags = 0
f3215127 426 self.lowerifaces = None
c798b0f4 427 self.upperifaces = None
d08d5f54 428 self.linkstate = None
5c721925 429 self.env = None
430 self.priv_flags = 0
62ddec8b 431 self.raw_config = []
432 self.flags |= self._PICKLED
f3b69969 433 self.link_type = ifaceLinkType.LINK_NA
a9ab1b4f 434 self.link_kind = ifaceLinkKind.UNKNOWN
3dcc1d0e 435
a6f80f0e 436 def dump_raw(self, logger):
437 indent = ' '
53b00224 438 if self.auto:
439 print 'auto %s' %self.name
62ddec8b 440 print (self.raw_config[0])
441 for i in range(1, len(self.raw_config)):
53b00224 442 print(indent + self.raw_config[i])
a6f80f0e 443
a6f80f0e 444 def dump(self, logger):
445 indent = '\t'
62ddec8b 446 logger.info(self.name + ' : {')
447 logger.info(indent + 'family: %s' %self.addr_family)
448 logger.info(indent + 'method: %s' %self.addr_method)
31a5f4c3 449 logger.info(indent + 'flags: %x' %self.flags)
a6f80f0e 450 logger.info(indent + 'state: %s'
62ddec8b 451 %ifaceState.to_str(self.state))
a6f80f0e 452 logger.info(indent + 'status: %s'
62ddec8b 453 %ifaceStatus.to_str(self.status))
454 logger.info(indent + 'refcnt: %d' %self.refcnt)
455 d = self.lowerifaces
fe0a57d3 456 if d:
f3215127 457 logger.info(indent + 'lowerdevs: %s' %str(d))
a6f80f0e 458 else:
f3215127 459 logger.info(indent + 'lowerdevs: None')
739f665b 460
a6f80f0e 461 logger.info(indent + 'config: ')
62ddec8b 462 config = self.config
fe0a57d3 463 if config:
a6f80f0e 464 logger.info(indent + indent + str(config))
465 logger.info('}')
466
86fc62e2 467 def dump_pretty(self, with_status=False,
2da58137 468 successstr='success', errorstr='error',
fac4138b 469 unknownstr='unknown', use_realname=False):
a6f80f0e 470 indent = '\t'
cca03c30 471 outbuf = ''
b48ff1a9
RP
472 if use_realname and self.realname:
473 name = '%s' %self.realname
2da58137 474 else:
b48ff1a9 475 name = '%s' %self.name
62ddec8b 476 if self.auto:
2da58137 477 outbuf += 'auto %s\n' %name
5b4d3044 478 ifaceline = ''
cb46a208 479 if self.type == ifaceType.BRIDGE_VLAN:
5b4d3044 480 ifaceline += 'vlan %s' %name
cb46a208 481 else:
5b4d3044 482 ifaceline += 'iface %s' %name
62ddec8b 483 if self.addr_family:
5b4d3044 484 ifaceline += ' %s' %self.addr_family
62ddec8b 485 if self.addr_method:
5b4d3044 486 ifaceline += ' %s' %self.addr_method
2cd06f78 487 if with_status:
5b4d3044 488 status_str = None
e1601369
RP
489 if (self.status == ifaceStatus.ERROR or
490 self.status == ifaceStatus.NOTFOUND):
491 if self.status_str:
5b4d3044
RP
492 ifaceline += ' (%s)' %self.status_str
493 status_str = errorstr
86fc62e2 494 elif self.status == ifaceStatus.SUCCESS:
5b4d3044
RP
495 status_str = successstr
496 if status_str:
497 outbuf += '{0:65} {1:>8}'.format(ifaceline, status_str) + '\n'
498 else:
499 outbuf += ifaceline + '\n'
2cd06f78 500 if self.status == ifaceStatus.NOTFOUND:
e1601369
RP
501 outbuf = (outbuf.encode('utf8')
502 if isinstance(outbuf, unicode) else outbuf)
2cd06f78 503 print outbuf + '\n'
504 return
5b4d3044
RP
505 else:
506 outbuf += ifaceline + '\n'
62ddec8b 507 config = self.config
fe0a57d3 508 if config:
eab25b7c 509 for cname, cvaluelist in config.items():
69f58278 510 idx = 0
eab25b7c 511 for cv in cvaluelist:
69f58278 512 if with_status:
86fc62e2 513 s = self.get_config_attr_status(cname, idx)
e1601369 514 if s == -1:
5b4d3044 515 status_str = unknownstr
e1601369 516 elif s == 1:
5b4d3044 517 status_str = errorstr
86fc62e2 518 elif s == 0:
5b4d3044
RP
519 status_str = successstr
520 outbuf += (indent + '{0:55} {1:>10}'.format(
521 '%s %s' %(cname, cv), status_str)) + '\n'
69f58278 522 else:
523 outbuf += indent + '%s %s\n' %(cname, cv)
524 idx += 1
3dcc1d0e 525 if with_status:
526 outbuf = (outbuf.encode('utf8')
527 if isinstance(outbuf, unicode) else outbuf)
a6f80f0e 528 print outbuf