]>
Commit | Line | Data |
---|---|---|
a6f80f0e | 1 | #!/usr/bin/python |
3e8ee54f | 2 | # |
d486dd0d | 3 | # Copyright 2014-2017 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 | 12 | It is modeled based on the 'iface' section in /etc/network/interfaces |
62ddec8b | 13 | file. But can be extended to include any other network interface format |
62ddec8b | 14 | """ |
15 | ||
2cd06f78 | 16 | import json |
a6f80f0e | 17 | |
d486dd0d JF |
18 | from collections import OrderedDict |
19 | ||
20 | ||
307e06bb RP |
21 | class ifaceStatusUserStrs(): |
22 | """ This class declares strings user can see during an ifquery --check | |
23 | for example. These strings can be overridden by user defined strings from | |
24 | config file """ | |
fb10449e RP |
25 | SUCCESS = "success", |
26 | FAILURE = "error", | |
27 | UNKNOWN = "unknown" | |
28 | ||
41febf89 | 29 | class ifaceType(): |
858a230f RP |
30 | UNKNOWN = 0x00 |
31 | IFACE = 0x01 | |
32 | BRIDGE_VLAN = 0x10 | |
0a3bee28 ST |
33 | |
34 | class ifaceRole(): | |
35 | """ ifaceRole is used to classify the ifaceobj.role of | |
36 | MASTER or SLAVE where there is a bond or bridge | |
37 | with bond-slaves or bridge-ports. A bond in a bridge | |
38 | is both a master and slave (0x3) | |
39 | """ | |
858a230f RP |
40 | UNKNOWN = 0x00 |
41 | SLAVE = 0x01 | |
42 | MASTER = 0x10 | |
0a3bee28 | 43 | |
a9ab1b4f | 44 | class ifaceLinkKind(): |
0a3bee28 ST |
45 | """ ifaceLlinkKind is used to identify interfaces |
46 | in the ifaceobj.link_kind attribute. Dependents of the bridge or | |
47 | bond have an ifaceobj.role attribute of SLAVE and the bridge or | |
48 | bond itself has ifaceobj.role of MASTER. | |
49 | """ | |
b1a2d241 MW |
50 | UNKNOWN = 0x0000000 |
51 | BRIDGE = 0x0000001 | |
52 | BOND = 0x0000010 | |
53 | VLAN = 0x0000100 | |
54 | VXLAN = 0x0001000 | |
55 | VRF = 0x0010000 | |
56 | BATMAN_ADV = 0x0100000 | |
d486dd0d JF |
57 | # to indicate logical interface created by an external entity. |
58 | # the 'kind' of which ifupdown2 does not really understand | |
b1a2d241 | 59 | OTHER = 0x1000000 |
d486dd0d JF |
60 | |
61 | @classmethod | |
62 | def to_str(cls, kind): | |
63 | if kind == cls.BRIDGE: | |
64 | return "bridge" | |
65 | elif kind == cls.BOND: | |
66 | return "bond" | |
67 | elif kind == cls.VLAN: | |
68 | return "vlan" | |
69 | elif kind == cls.VXLAN: | |
70 | return "vxlan" | |
71 | elif kind == cls.VRF: | |
72 | return "vrf" | |
858a230f RP |
73 | |
74 | class ifaceLinkPrivFlags(): | |
75 | """ This corresponds to kernel netdev->priv_flags | |
76 | and can be BRIDGE_PORT, BOND_SLAVE etc """ | |
ea9e3c0f JF |
77 | UNKNOWN = 0x00000 |
78 | BRIDGE_PORT = 0x00001 | |
79 | BOND_SLAVE = 0x00010 | |
80 | VRF_SLAVE = 0x00100 | |
81 | BRIDGE_VLAN_AWARE = 0x01000 | |
82 | BRIDGE_VXLAN = 0x10000 | |
42e85fc8 | 83 | ADDRESS_VIRTUAL_SLAVE = 0x100000 |
ae330408 | 84 | LOOPBACK = 0x1000000 |
9858b0a6 | 85 | KEEP_LINK_DOWN = 0x10000000 |
858a230f RP |
86 | |
87 | @classmethod | |
88 | def get_str(cls, flag): | |
89 | if flag == cls.UNKNOWN: | |
90 | return 'unknown' | |
91 | elif flag == cls.BRIDGE_PORT: | |
92 | return 'bridge port' | |
93 | elif flag == cls.BOND_SLAVE: | |
94 | return 'bond slave' | |
95 | elif flag == cls.VRF_SLAVE: | |
96 | return 'vrf slave' | |
97 | elif flag == cls.BRIDGE_VLAN_AWARE: | |
98 | return 'vlan aware bridge' | |
ea9e3c0f JF |
99 | elif flag == cls.BRIDGE_VXLAN: |
100 | return 'vxlan bridge' | |
858a230f RP |
101 | |
102 | @classmethod | |
103 | def get_all_str(cls, flags): | |
104 | str = '' | |
ea9e3c0f | 105 | if flags & cls.BRIDGE_PORT: |
858a230f | 106 | str += 'bridgeport ' |
ea9e3c0f | 107 | if flags & cls.BOND_SLAVE: |
858a230f | 108 | str += 'bondslave ' |
ea9e3c0f | 109 | if flags & cls.VRF_SLAVE: |
858a230f | 110 | str += 'vrfslave ' |
ea9e3c0f | 111 | if flags & cls.BRIDGE_VLAN_AWARE: |
858a230f | 112 | str += 'vlanawarebridge ' |
ea9e3c0f JF |
113 | if flags & cls.BRIDGE_VXLAN: |
114 | str += 'vxlanbridge ' | |
858a230f | 115 | return str |
a9ab1b4f | 116 | |
c416da6a | 117 | class ifaceLinkType(): |
a070c90e | 118 | LINK_UNKNOWN = 0x0 |
c416da6a RP |
119 | LINK_SLAVE = 0x1 |
120 | LINK_MASTER = 0x2 | |
a070c90e | 121 | LINK_NA = 0x3 |
c416da6a | 122 | |
d486dd0d JF |
123 | class VlanProtocols(): |
124 | # Picked ID values from | |
125 | # http://www.microhowto.info/howto/configure_an_ethernet_interface_as_a_qinq_vlan_trunk.html | |
126 | ETHERTYPES_TO_ID = { | |
127 | '802.1Q' : '0x8100', | |
128 | '802.1AD' : '0x88a8', | |
129 | } | |
130 | ID_TO_ETHERTYPES = { | |
131 | '0x8100' : '802.1Q', | |
132 | '0x88a8' : '802.1AD', | |
133 | } | |
134 | ||
45ca0b6d RP |
135 | class ifaceDependencyType(): |
136 | """ Indicates type of dependency. | |
137 | ||
138 | This class enumerates types of dependency relationships | |
139 | between interfaces. | |
140 | ||
141 | iface dependency relationships can be classified | |
142 | into: | |
143 | - link | |
144 | - master/slave | |
145 | ||
146 | In a 'link' dependency relationship, dependency can be shared | |
147 | between interfaces. example: swp1.100 and | |
148 | swp1.200 can both have 'link' swp1. swp1 is also a dependency | |
149 | of swp1.100 and swp1.200. As you can see dependency | |
150 | swp1 is shared between swp1.100 and swp1.200. | |
d486dd0d | 151 | |
45ca0b6d RP |
152 | In a master/slave relationship like bridge and |
153 | its ports: eg: bridge br0 and its ports swp1 and swp2. | |
154 | dependency swp1 and swp2 cannot be shared with any other | |
155 | interface with the same dependency relationship. | |
156 | ie, swp1 and swp2 cannot be in a slave relationship | |
157 | with another interface. Understanding the dependency type is | |
158 | required for any semantic checks between dependencies. | |
159 | ||
160 | """ | |
161 | UNKNOWN = 0x0 | |
162 | LINK = 0x1 | |
163 | MASTER_SLAVE = 0x2 | |
164 | ||
a6f80f0e | 165 | class ifaceStatus(): |
62ddec8b | 166 | """Enumerates iface status """ |
167 | ||
a6f80f0e | 168 | UNKNOWN = 0x1 |
169 | SUCCESS = 0x2 | |
ceed018d RP |
170 | WARNING = 0x3 |
171 | ERROR = 0x4 | |
172 | NOTFOUND = 0x5 | |
a6f80f0e | 173 | |
174 | @classmethod | |
175 | def to_str(cls, state): | |
176 | if state == cls.UNKNOWN: | |
177 | return 'unknown' | |
178 | elif state == cls.SUCCESS: | |
179 | return 'success' | |
180 | elif state == cls.ERROR: | |
181 | return 'error' | |
739f665b | 182 | elif state == cls.NOTFOUND: |
69f58278 | 183 | return 'notfound' |
d486dd0d | 184 | |
a6f80f0e | 185 | @classmethod |
186 | def from_str(cls, state_str): | |
187 | if state_str == 'unknown': | |
188 | return cls.UNKNOWN | |
189 | elif state_str == 'success': | |
190 | return cls.SUCCESS | |
191 | elif state_str == 'error': | |
192 | return cls.ERROR | |
193 | ||
194 | class ifaceState(): | |
62ddec8b | 195 | """Enumerates iface state """ |
a6f80f0e | 196 | |
a6f80f0e | 197 | UNKNOWN = 0x1 |
198 | NEW = 0x2 | |
199 | PRE_UP = 0x3 | |
200 | UP = 0x4 | |
201 | POST_UP = 0x5 | |
202 | PRE_DOWN = 0x6 | |
203 | DOWN = 0x7 | |
204 | POST_DOWN = 0x8 | |
205 | ||
d08d5f54 | 206 | # Pseudo states |
207 | QUERY_CHECKCURR = 0x9 | |
208 | QUERY_RUNNING = 0xa | |
209 | ||
a6f80f0e | 210 | @classmethod |
211 | def to_str(cls, state): | |
212 | if state == cls.UNKNOWN: | |
213 | return 'unknown' | |
214 | elif state == cls.NEW: | |
215 | return 'new' | |
216 | elif state == cls.PRE_UP: | |
217 | return 'pre-up' | |
218 | elif state == cls.UP: | |
219 | return 'up' | |
220 | elif state == cls.POST_UP: | |
221 | return 'post-up' | |
222 | elif state == cls.PRE_DOWN: | |
223 | return 'pre-down' | |
d08d5f54 | 224 | elif state == cls.DOWN: |
225 | return 'down' | |
a6f80f0e | 226 | elif state == cls.POST_DOWN: |
227 | return 'post-down' | |
d08d5f54 | 228 | elif state == cls.QUERY_CHECKCURR: |
229 | return 'query-checkcurr' | |
230 | elif state == cls.QUERY_RUNNING: | |
231 | return 'query-running' | |
a6f80f0e | 232 | |
233 | @classmethod | |
234 | def from_str(cls, state_str): | |
235 | if state_str == 'unknown': | |
236 | return cls.UNKNOWN | |
237 | elif state_str == 'new': | |
238 | return cls.NEW | |
239 | elif state_str == 'pre-up': | |
240 | return cls.PRE_UP | |
241 | elif state_str == 'up': | |
242 | return cls.UP | |
243 | elif state_str == 'post-up': | |
244 | return cls.POST_UP | |
245 | elif state_str == 'pre-down': | |
246 | return cls.PRE_DOWN | |
d08d5f54 | 247 | elif state_str == 'down': |
248 | return cls.DOWN | |
a6f80f0e | 249 | elif state_str == 'post-down': |
250 | return cls.POST_DOWN | |
d08d5f54 | 251 | elif state_str == 'query-checkcurr': |
252 | return cls.QUERY_CHECKCURR | |
253 | elif state_str == 'query-running': | |
254 | return cls.QUERY_RUNNING | |
a6f80f0e | 255 | |
d08d5f54 | 256 | class ifaceJsonEncoder(json.JSONEncoder): |
f012209e | 257 | def default(self, o): |
83c1f241 | 258 | retconfig = {} |
fb10449e | 259 | retifacedict = OrderedDict([]) |
d486dd0d | 260 | if o.config: |
f012209e RP |
261 | retconfig = dict((k, (v[0] if len(v) == 1 else v)) |
262 | for k,v in o.config.items()) | |
263 | retifacedict['name'] = o.name | |
264 | if o.addr_method: | |
b46f4b05 JF |
265 | if 'inet' in o.addr_family and 'dhcp' in o.addr_method: |
266 | retifacedict['addr_method'] = 'dhcp' | |
267 | else: | |
268 | retifacedict['addr_method'] = o.addr_method | |
f012209e | 269 | if o.addr_family: |
b46f4b05 JF |
270 | if len(o.addr_family) > 1: |
271 | retifacedict['addr_family'] = o.addr_family | |
272 | else: | |
273 | retifacedict['addr_family'] = ' '.join(o.addr_family) | |
f012209e RP |
274 | retifacedict['auto'] = o.auto |
275 | retifacedict['config'] = retconfig | |
276 | ||
277 | return retifacedict | |
278 | ||
279 | class ifaceJsonEncoderWithStatus(json.JSONEncoder): | |
280 | def default(self, o): | |
281 | retconfig = {} | |
282 | retconfig_status = {} | |
283 | retifacedict = OrderedDict([]) | |
284 | if o.config: | |
fb10449e RP |
285 | for k,v in o.config.items(): |
286 | idx = 0 | |
287 | vitem_status = [] | |
288 | for vitem in v: | |
289 | s = o.get_config_attr_status(k, idx) | |
d486dd0d | 290 | if s == 1: |
307e06bb | 291 | status_str = ifaceStatusUserStrs.ERROR |
fb10449e | 292 | elif s == 0: |
307e06bb | 293 | status_str = ifaceStatusUserStrs.SUCCESS |
d486dd0d JF |
294 | else: |
295 | status_str = ifaceStatusUserStrs.UNKNOWN | |
fb10449e RP |
296 | vitem_status.append('%s' %status_str) |
297 | idx += 1 | |
298 | retconfig[k] = v[0] if len(v) == 1 else v | |
299 | retconfig_status[k] = vitem_status[0] if len(vitem_status) == 1 else vitem_status | |
300 | ||
301 | if (o.status == ifaceStatus.NOTFOUND or | |
302 | o.status == ifaceStatus.ERROR): | |
307e06bb | 303 | status = ifaceStatusUserStrs.ERROR |
fb10449e | 304 | else: |
307e06bb | 305 | status = ifaceStatusUserStrs.SUCCESS |
fb10449e RP |
306 | |
307 | retifacedict['name'] = o.name | |
308 | if o.addr_method: | |
309 | retifacedict['addr_method'] = o.addr_method | |
310 | if o.addr_family: | |
b46f4b05 JF |
311 | if len(o.addr_family) > 1: |
312 | retifacedict['addr_family'] = o.addr_family | |
313 | else: | |
314 | retifacedict['addr_family'] = ' '.join(o.addr_family) | |
fb10449e RP |
315 | retifacedict['auto'] = o.auto |
316 | retifacedict['config'] = retconfig | |
317 | retifacedict['config_status'] = retconfig_status | |
318 | retifacedict['status'] = status | |
319 | ||
320 | return retifacedict | |
d08d5f54 | 321 | |
3dcc1d0e | 322 | class ifaceJsonDecoder(): |
323 | @classmethod | |
324 | def json_to_ifaceobj(cls, ifaceattrdict): | |
325 | ifaceattrdict['config'] = OrderedDict([(k, (v if isinstance(v, list) | |
fc68074f | 326 | else [v.strip()])) |
3dcc1d0e | 327 | for k,v in ifaceattrdict.get('config', |
328 | OrderedDict()).items()]) | |
329 | return iface(attrsdict=ifaceattrdict) | |
330 | ||
a6f80f0e | 331 | class iface(): |
904908bc | 332 | """ ifupdown2 iface object class |
62ddec8b | 333 | Attributes: |
d486dd0d | 334 | **name** Name of the interface |
904908bc RP |
335 | |
336 | **addr_family** Address family eg, inet, inet6. Can be None to | |
337 | indicate both address families | |
338 | ||
339 | **addr_method** Address method eg, static, manual or None for | |
340 | static address method | |
341 | ||
342 | **config** dictionary of config lines for this interface | |
343 | ||
344 | **state** Configuration state of an interface as defined by | |
345 | ifaceState | |
346 | ||
347 | **status** Configuration status of an interface as defined by | |
348 | ifaceStatus | |
349 | ||
350 | **flags** Internal flags used by iface processing | |
351 | ||
352 | **priv_flags** private flags owned by module using this class | |
353 | ||
4c773918 ST |
354 | **module_flags** module flags owned by module using this class |
355 | ||
904908bc RP |
356 | **refcnt** reference count, indicating number of interfaces |
357 | dependent on this iface | |
358 | ||
359 | **lowerifaces** list of interface names lower to this interface or | |
360 | this interface depends on | |
361 | ||
362 | **upperifaces** list of interface names upper to this interface or | |
d486dd0d | 363 | the interfaces that depend on this interface |
904908bc RP |
364 | |
365 | **auto** True if interface belongs to the auto class | |
366 | ||
367 | **classes** List of classes the interface belongs to | |
368 | ||
369 | **env** shell environment the interface needs during | |
370 | execution | |
371 | ||
372 | **raw_config** raw interface config from file | |
62ddec8b | 373 | """ |
374 | ||
31a5f4c3 | 375 | # flag to indicate that the object was created from pickled state |
0582f185 | 376 | # XXX: Move these flags into a separate iface flags class |
7444feea ST |
377 | _PICKLED = 0x00000001 |
378 | HAS_SIBLINGS = 0x00000010 | |
73c27592 RP |
379 | IFACERANGE_ENTRY = 0x00000100 |
380 | IFACERANGE_START = 0x00001000 | |
7444feea | 381 | OLDEST_SIBLING = 0x00010000 |
0582f185 | 382 | YOUNGEST_SIBLING = 0x00100000 |
a6f80f0e | 383 | |
37c0543d | 384 | version = '0.1' |
385 | ||
3dcc1d0e | 386 | def __init__(self, attrsdict={}): |
004d1e65 JF |
387 | self.addr_family = [] |
388 | ||
3dcc1d0e | 389 | self._set_attrs_from_dict(attrsdict) |
62ddec8b | 390 | self._config_status = {} |
2c0ad8b3 | 391 | """dict with config status of iface attributes""" |
a6f80f0e | 392 | self.state = ifaceState.NEW |
2c0ad8b3 | 393 | """iface state (of type ifaceState) """ |
a6f80f0e | 394 | self.status = ifaceStatus.UNKNOWN |
2c0ad8b3 | 395 | """iface status (of type ifaceStatus) """ |
e1601369 RP |
396 | self.status_str = None |
397 | """iface status str (string representing the status) """ | |
a6f80f0e | 398 | self.flags = 0x0 |
2c0ad8b3 | 399 | """iface flags """ |
dbc018d3 | 400 | self.priv_flags = None |
4c773918 ST |
401 | """iface module flags dictionary with module name: flags""" |
402 | self.module_flags = {} | |
2c0ad8b3 | 403 | """iface priv flags. can be used by the external object manager """ |
a6f80f0e | 404 | self.refcnt = 0 |
2c0ad8b3 | 405 | """iface refcnt (incremented for each dependent this interface has) """ |
d486dd0d | 406 | self.lowerifaces = None |
2c0ad8b3 | 407 | """lower iface list (in other words: slaves of this interface """ |
f3215127 | 408 | self.upperifaces = None |
2c0ad8b3 | 409 | """upper iface list (in other words: master of this interface """ |
a6f80f0e | 410 | self.classes = [] |
2c0ad8b3 | 411 | """interface classes this iface belongs to """ |
a6f80f0e | 412 | self.env = None |
2c0ad8b3 | 413 | """environment variable dict required for this interface to run""" |
62ddec8b | 414 | self.raw_config = [] |
2c0ad8b3 | 415 | """interface config/attributes in raw format (eg: as it appeared in the interfaces file)""" |
a6f80f0e | 416 | self.linkstate = None |
2c0ad8b3 | 417 | """linkstate of the interface""" |
41febf89 RP |
418 | self.type = ifaceType.UNKNOWN |
419 | """interface type""" | |
420 | self.priv_data = None | |
0a3bee28 | 421 | self.role = ifaceRole.UNKNOWN |
2da58137 | 422 | self.realname = None |
a070c90e | 423 | self.link_type = ifaceLinkType.LINK_UNKNOWN |
c65c2ccd | 424 | self.link_kind = ifaceLinkKind.UNKNOWN |
858a230f | 425 | self.link_privflags = ifaceLinkPrivFlags.UNKNOWN |
a6f80f0e | 426 | |
45ca0b6d RP |
427 | # The below attribute is used to disambiguate between various |
428 | # types of dependencies | |
429 | self.dependency_type = ifaceDependencyType.UNKNOWN | |
0532f0a3 | 430 | self.blacklisted = False |
45ca0b6d | 431 | |
3dcc1d0e | 432 | def _set_attrs_from_dict(self, attrdict): |
433 | self.auto = attrdict.get('auto', False) | |
434 | self.name = attrdict.get('name') | |
3dcc1d0e | 435 | self.addr_method = attrdict.get('addr_method') |
436 | self.config = attrdict.get('config', OrderedDict()) | |
437 | ||
004d1e65 JF |
438 | addr_family = attrdict.get('addr_family') |
439 | if addr_family: | |
440 | self.addr_family.append(addr_family) | |
441 | ||
a6f80f0e | 442 | def inc_refcnt(self): |
2c0ad8b3 RP |
443 | """ increment refcnt of the interface. Usually used to indicate that |
444 | it has dependents """ | |
a6f80f0e | 445 | self.refcnt += 1 |
446 | ||
447 | def dec_refcnt(self): | |
2c0ad8b3 RP |
448 | """ decrement refcnt of the interface. Usually used to indicate that |
449 | it has lost its dependent """ | |
a6f80f0e | 450 | self.refcnt -= 1 |
451 | ||
cca03c30 | 452 | def is_config_present(self): |
2c0ad8b3 RP |
453 | """ returns true if the interface has user provided config, |
454 | false otherwise """ | |
62ddec8b | 455 | addr_method = self.addr_method |
2cd06f78 | 456 | if addr_method and addr_method in ['dhcp', 'dhcp6', 'loopback']: |
457 | return True | |
fe0a57d3 | 458 | if not self.config: |
cca03c30 | 459 | return False |
fe0a57d3 | 460 | else: |
461 | return True | |
cca03c30 | 462 | |
3e8ee54f | 463 | def set_class(self, classname): |
2c0ad8b3 | 464 | """ appends class to the interfaces class list """ |
a6f80f0e | 465 | self.classes.append(classname) |
466 | ||
31a5f4c3 | 467 | def set_state_n_status(self, state, status): |
2c0ad8b3 | 468 | """ sets state and status of an interface """ |
31a5f4c3 | 469 | self.state = state |
ceed018d RP |
470 | if status > self.status: |
471 | self.status = status | |
472 | ||
473 | def set_status(self, status): | |
474 | """ sets status of an interface """ | |
475 | if status > self.status: | |
476 | self.status = status | |
31a5f4c3 | 477 | |
a6f80f0e | 478 | def set_flag(self, flag): |
479 | self.flags |= flag | |
480 | ||
481 | def clear_flag(self, flag): | |
482 | self.flags &= ~flag | |
483 | ||
f3215127 | 484 | def add_to_upperifaces(self, upperifacename): |
2c0ad8b3 | 485 | """ add to the list of upperifaces """ |
f3215127 | 486 | if self.upperifaces: |
487 | if upperifacename not in self.upperifaces: | |
488 | self.upperifaces.append(upperifacename) | |
489 | else: | |
490 | self.upperifaces = [upperifacename] | |
491 | ||
4cc2df04 RP |
492 | def add_to_lowerifaces(self, lowerifacename): |
493 | """ add to the list of lowerifaces """ | |
494 | if self.lowerifaces: | |
495 | if lowerifacename not in self.lowerifaces: | |
496 | self.lowerifaces.append(lowerifacename) | |
497 | else: | |
498 | self.lowerifaces = [lowerifacename] | |
499 | ||
a6f80f0e | 500 | def get_attr_value(self, attr_name): |
2c0ad8b3 | 501 | """ add to the list of upperifaces """ |
62ddec8b | 502 | return self.config.get(attr_name) |
d486dd0d | 503 | |
a6f80f0e | 504 | def get_attr_value_first(self, attr_name): |
2c0ad8b3 | 505 | """ get first value of the specified attr name """ |
62ddec8b | 506 | attr_value_list = self.config.get(attr_name) |
fe0a57d3 | 507 | if attr_value_list: |
a6f80f0e | 508 | return attr_value_list[0] |
a6f80f0e | 509 | return None |
510 | ||
679e6567 RP |
511 | def get_attrs_value_first(self, attrs): |
512 | """ get first value of the first attr in the list. | |
513 | Useful when you have multiple attrs representing the | |
514 | same thing. | |
515 | """ | |
516 | for attr in attrs: | |
517 | attr_value_list = self.config.get(attr) | |
518 | if attr_value_list: | |
519 | return attr_value_list[0] | |
520 | return None | |
521 | ||
a6f80f0e | 522 | def get_attr_value_n(self, attr_name, attr_index): |
2c0ad8b3 | 523 | """ get n'th value of the specified attr name """ |
62ddec8b | 524 | attr_value_list = self.config.get(attr_name) |
fe0a57d3 | 525 | if attr_value_list: |
a6f80f0e | 526 | try: |
527 | return attr_value_list[attr_index] | |
528 | except: | |
529 | return None | |
a6f80f0e | 530 | return None |
531 | ||
532 | def get_env(self): | |
2c0ad8b3 | 533 | """ get shell environment variables the interface must execute in """ |
fe0a57d3 | 534 | if not self.env: |
a6f80f0e | 535 | self.generate_env() |
536 | return self.env | |
537 | ||
a6f80f0e | 538 | def generate_env(self): |
2c0ad8b3 RP |
539 | """ generate shell environment variables dict interface must execute |
540 | in. This is used to support legacy ifupdown scripts | |
541 | """ | |
a6f80f0e | 542 | env = {} |
62ddec8b | 543 | config = self.config |
544 | env['IFACE'] = self.name | |
a6f80f0e | 545 | for attr, attr_value in config.items(): |
60dfcbdf | 546 | attr_env_name = 'IF_%s' %attr.upper().replace("-", "_") |
a6f80f0e | 547 | env[attr_env_name] = attr_value[0] |
60dfcbdf | 548 | self.env = env |
a6f80f0e | 549 | |
739f665b | 550 | def update_config(self, attr_name, attr_value): |
2c0ad8b3 | 551 | """ add attribute name and value to the interface config """ |
62ddec8b | 552 | self.config.setdefault(attr_name, []).append(attr_value) |
739f665b | 553 | |
d8e3554d RP |
554 | def replace_config(self, attr_name, attr_value): |
555 | """ add attribute name and value to the interface config """ | |
556 | self.config[attr_name] = [attr_value] | |
557 | ||
558 | def delete_config(self, attr_name): | |
559 | """ add attribute name and value to the interface config """ | |
560 | try: | |
561 | del self.config[attr_name] | |
562 | except: | |
563 | pass | |
564 | ||
739f665b | 565 | def update_config_dict(self, attrdict): |
566 | self.config.update(attrdict) | |
567 | ||
568 | def update_config_with_status(self, attr_name, attr_value, attr_status=0): | |
2c0ad8b3 RP |
569 | """ add attribute name and value to the interface config and also |
570 | update the config_status dict with status of this attribute config """ | |
69f58278 | 571 | if not attr_value: |
a6f80f0e | 572 | attr_value = '' |
62ddec8b | 573 | self.config.setdefault(attr_name, []).append(attr_value) |
574 | self._config_status.setdefault(attr_name, []).append(attr_status) | |
69f58278 | 575 | # set global iface state |
e1601369 | 576 | if attr_status == 1: |
62ddec8b | 577 | self.status = ifaceStatus.ERROR |
578 | elif self.status != ifaceStatus.ERROR: | |
69f58278 | 579 | # Not already error, mark success |
62ddec8b | 580 | self.status = ifaceStatus.SUCCESS |
69f58278 | 581 | |
a070c90e | 582 | def check_n_update_config_with_status_many(self, ifaceobjorig, attr_names, |
e1601369 RP |
583 | attr_status=0): |
584 | # set multiple attribute status to zero | |
585 | # also updates status only if the attribute is present | |
586 | for attr_name in attr_names: | |
a070c90e RP |
587 | if not ifaceobjorig.get_attr_value_first(attr_name): |
588 | continue | |
e1601369 RP |
589 | self.config.setdefault(attr_name, []).append('') |
590 | self._config_status.setdefault(attr_name, []).append(attr_status) | |
591 | ||
69f58278 | 592 | def get_config_attr_status(self, attr_name, idx=0): |
2c0ad8b3 | 593 | """ get status of a attribute config on this interface. |
d486dd0d | 594 | |
2c0ad8b3 | 595 | Looks at the iface _config_status dict""" |
62ddec8b | 596 | return self._config_status.get(attr_name, [])[idx] |
69f58278 | 597 | |
ca3f4fc7 | 598 | def compare(self, dstiface): |
2c0ad8b3 | 599 | """ compares iface object with iface object passed as argument |
ca3f4fc7 | 600 | |
601 | Returns True if object self is same as dstiface and False otherwise """ | |
602 | ||
603 | if self.name != dstiface.name: return False | |
84ca006f | 604 | if self.type != dstiface.type: return False |
ca3f4fc7 | 605 | if self.addr_family != dstiface.addr_family: return False |
606 | if self.addr_method != dstiface.addr_method: return False | |
607 | if self.auto != dstiface.auto: return False | |
608 | if self.classes != dstiface.classes: return False | |
acf92644 RP |
609 | if len(self.config) != len(dstiface.config): |
610 | return False | |
37c0543d | 611 | if any(True for k in self.config if k not in dstiface.config): |
ca3f4fc7 | 612 | return False |
37c0543d | 613 | if any(True for k,v in self.config.items() |
ca3f4fc7 | 614 | if v != dstiface.config.get(k)): return False |
615 | return True | |
d08d5f54 | 616 | |
99ce6894 RP |
617 | def squash(self, newifaceobj): |
618 | """ This squashes the iface object """ | |
619 | for attrname, attrlist in newifaceobj.config.iteritems(): | |
620 | # if allready present add it to the list | |
621 | # else add it to the end of the dictionary | |
622 | # We need to maintain order. | |
623 | if self.config.get(attrname): | |
624 | self.config[attrname].extend(attrlist) | |
625 | else: | |
626 | self.config.update([(attrname, attrlist)]) | |
004d1e65 JF |
627 | # we now support inet and inet6 together |
628 | self.addr_family.extend(newifaceobj.addr_family) | |
05bfbb9a JF |
629 | # if auto %ifacename is not part of the first stanza |
630 | # we need to squash it | |
631 | if not self.auto and newifaceobj.auto: | |
632 | self.auto = True | |
e1601369 | 633 | |
d08d5f54 | 634 | def __getstate__(self): |
635 | odict = self.__dict__.copy() | |
636 | del odict['state'] | |
637 | del odict['status'] | |
f3215127 | 638 | del odict['lowerifaces'] |
c798b0f4 | 639 | del odict['upperifaces'] |
d08d5f54 | 640 | del odict['refcnt'] |
62ddec8b | 641 | del odict['_config_status'] |
5c721925 | 642 | del odict['flags'] |
643 | del odict['priv_flags'] | |
4c773918 | 644 | del odict['module_flags'] |
62ddec8b | 645 | del odict['raw_config'] |
5c721925 | 646 | del odict['linkstate'] |
647 | del odict['env'] | |
c416da6a | 648 | del odict['link_type'] |
a9ab1b4f | 649 | del odict['link_kind'] |
858a230f | 650 | del odict['link_privflags'] |
0a3bee28 | 651 | del odict['role'] |
45ca0b6d | 652 | del odict['dependency_type'] |
0532f0a3 | 653 | del odict['blacklisted'] |
d08d5f54 | 654 | return odict |
655 | ||
656 | def __setstate__(self, dict): | |
657 | self.__dict__.update(dict) | |
62ddec8b | 658 | self._config_status = {} |
d08d5f54 | 659 | self.state = ifaceState.NEW |
660 | self.status = ifaceStatus.UNKNOWN | |
661 | self.refcnt = 0 | |
5c721925 | 662 | self.flags = 0 |
f3215127 | 663 | self.lowerifaces = None |
c798b0f4 | 664 | self.upperifaces = None |
d08d5f54 | 665 | self.linkstate = None |
5c721925 | 666 | self.env = None |
0a3bee28 | 667 | self.role = ifaceRole.UNKNOWN |
dbc018d3 | 668 | self.priv_flags = None |
4c773918 | 669 | self.module_flags = {} |
62ddec8b | 670 | self.raw_config = [] |
671 | self.flags |= self._PICKLED | |
f3b69969 | 672 | self.link_type = ifaceLinkType.LINK_NA |
a9ab1b4f | 673 | self.link_kind = ifaceLinkKind.UNKNOWN |
858a230f | 674 | self.link_privflags = ifaceLinkPrivFlags.UNKNOWN |
45ca0b6d | 675 | self.dependency_type = ifaceDependencyType.UNKNOWN |
0532f0a3 | 676 | self.blacklisted = False |
3dcc1d0e | 677 | |
a6f80f0e | 678 | def dump_raw(self, logger): |
679 | indent = ' ' | |
53b00224 | 680 | if self.auto: |
681 | print 'auto %s' %self.name | |
62ddec8b | 682 | print (self.raw_config[0]) |
683 | for i in range(1, len(self.raw_config)): | |
53b00224 | 684 | print(indent + self.raw_config[i]) |
d486dd0d | 685 | |
a6f80f0e | 686 | def dump(self, logger): |
687 | indent = '\t' | |
62ddec8b | 688 | logger.info(self.name + ' : {') |
004d1e65 | 689 | logger.info(indent + 'family: %s' % ' '.join(self.addr_family)) |
62ddec8b | 690 | logger.info(indent + 'method: %s' %self.addr_method) |
31a5f4c3 | 691 | logger.info(indent + 'flags: %x' %self.flags) |
a6f80f0e | 692 | logger.info(indent + 'state: %s' |
62ddec8b | 693 | %ifaceState.to_str(self.state)) |
a6f80f0e | 694 | logger.info(indent + 'status: %s' |
62ddec8b | 695 | %ifaceStatus.to_str(self.status)) |
696 | logger.info(indent + 'refcnt: %d' %self.refcnt) | |
697 | d = self.lowerifaces | |
fe0a57d3 | 698 | if d: |
f3215127 | 699 | logger.info(indent + 'lowerdevs: %s' %str(d)) |
a6f80f0e | 700 | else: |
f3215127 | 701 | logger.info(indent + 'lowerdevs: None') |
739f665b | 702 | |
ccbeedcd RP |
703 | d = self.upperifaces |
704 | if d: | |
705 | logger.info(indent + 'upperdevs: %s' %str(d)) | |
706 | else: | |
707 | logger.info(indent + 'upperdevs: None') | |
708 | ||
a6f80f0e | 709 | logger.info(indent + 'config: ') |
62ddec8b | 710 | config = self.config |
fe0a57d3 | 711 | if config: |
a6f80f0e | 712 | logger.info(indent + indent + str(config)) |
713 | logger.info('}') | |
714 | ||
004d1e65 | 715 | def _dump_pretty(self, family, first, addr_method, with_status=False, use_realname=False): |
a6f80f0e | 716 | indent = '\t' |
cca03c30 | 717 | outbuf = '' |
b48ff1a9 RP |
718 | if use_realname and self.realname: |
719 | name = '%s' %self.realname | |
2da58137 | 720 | else: |
b48ff1a9 | 721 | name = '%s' %self.name |
62ddec8b | 722 | if self.auto: |
2da58137 | 723 | outbuf += 'auto %s\n' %name |
5b4d3044 | 724 | ifaceline = '' |
cb46a208 | 725 | if self.type == ifaceType.BRIDGE_VLAN: |
5b4d3044 | 726 | ifaceline += 'vlan %s' %name |
cb46a208 | 727 | else: |
5b4d3044 | 728 | ifaceline += 'iface %s' %name |
004d1e65 JF |
729 | if family: |
730 | ifaceline += ' %s' % family | |
731 | if addr_method: | |
732 | ifaceline += ' %s' % addr_method | |
2cd06f78 | 733 | if with_status: |
5b4d3044 | 734 | status_str = None |
e1601369 RP |
735 | if (self.status == ifaceStatus.ERROR or |
736 | self.status == ifaceStatus.NOTFOUND): | |
737 | if self.status_str: | |
5b4d3044 | 738 | ifaceline += ' (%s)' %self.status_str |
307e06bb | 739 | status_str = '[%s]' %ifaceStatusUserStrs.ERROR |
86fc62e2 | 740 | elif self.status == ifaceStatus.SUCCESS: |
307e06bb | 741 | status_str = '[%s]' %ifaceStatusUserStrs.SUCCESS |
5b4d3044 | 742 | if status_str: |
004d1e65 | 743 | outbuf += '{0:65} {1:>8}'.format(ifaceline, status_str) + '\n' |
5b4d3044 RP |
744 | else: |
745 | outbuf += ifaceline + '\n' | |
2cd06f78 | 746 | if self.status == ifaceStatus.NOTFOUND: |
e1601369 RP |
747 | outbuf = (outbuf.encode('utf8') |
748 | if isinstance(outbuf, unicode) else outbuf) | |
2cd06f78 | 749 | print outbuf + '\n' |
750 | return | |
5b4d3044 RP |
751 | else: |
752 | outbuf += ifaceline + '\n' | |
62ddec8b | 753 | config = self.config |
004d1e65 | 754 | if config and first: |
eab25b7c | 755 | for cname, cvaluelist in config.items(): |
69f58278 | 756 | idx = 0 |
eab25b7c | 757 | for cv in cvaluelist: |
307e06bb | 758 | status_str = None |
69f58278 | 759 | if with_status: |
86fc62e2 | 760 | s = self.get_config_attr_status(cname, idx) |
e1601369 | 761 | if s == -1: |
307e06bb | 762 | status_str = '[%s]' %ifaceStatusUserStrs.UNKNOWN |
e1601369 | 763 | elif s == 1: |
307e06bb | 764 | status_str = '[%s]' %ifaceStatusUserStrs.ERROR |
86fc62e2 | 765 | elif s == 0: |
307e06bb RP |
766 | status_str = '[%s]' %ifaceStatusUserStrs.SUCCESS |
767 | if status_str: | |
5b4d3044 | 768 | outbuf += (indent + '{0:55} {1:>10}'.format( |
307e06bb | 769 | '%s %s' %(cname, cv), status_str)) + '\n' |
69f58278 | 770 | else: |
771 | outbuf += indent + '%s %s\n' %(cname, cv) | |
772 | idx += 1 | |
3dcc1d0e | 773 | if with_status: |
774 | outbuf = (outbuf.encode('utf8') | |
775 | if isinstance(outbuf, unicode) else outbuf) | |
a6f80f0e | 776 | print outbuf |
004d1e65 JF |
777 | |
778 | def dump_pretty(self, with_status=False, use_realname=False): | |
779 | if not self.addr_family: | |
780 | self._dump_pretty(None, True, | |
781 | self.addr_method, | |
782 | with_status=with_status, | |
783 | use_realname=use_realname) | |
784 | else: | |
b46f4b05 JF |
785 | # To allow both inet and inet6 on an interface we changed the |
786 | # addr_family attribute, it's now a list. Depending on how | |
787 | # stanzas were squashed (and what command was used ie. ifquery -r) | |
788 | # we want to dump the ifaceobj as usual but we will output an | |
789 | # empty stanza for each additional addr_family. The config will | |
790 | # only be displayed once, in the first stanza. Example: | |
791 | # $ ifquery eth0 -r | |
792 | # auto etho | |
793 | # iface eth0 inet dhcp | |
794 | # config... | |
795 | # | |
796 | # auto eth0 | |
797 | # iface eth0 inet6 dhcp | |
798 | # $ | |
004d1e65 JF |
799 | first = True |
800 | for family in self.addr_family: | |
b46f4b05 JF |
801 | addr_method = self.addr_method |
802 | # We need to make sure we display 'dhcp' for inet family. | |
803 | # In some cases it might take the value 'dhcp6' even if it has | |
804 | # both inet and inet6 addr_family | |
805 | if addr_method and family == 'inet' and 'dhcp' in addr_method: | |
806 | addr_method = 'dhcp' | |
807 | self._dump_pretty(family, first, | |
808 | addr_method=addr_method, | |
004d1e65 JF |
809 | with_status=with_status, |
810 | use_realname=use_realname) | |
811 | first = False |