]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdown/iface.py
bb095358997223f4a84431a0723de0aa9906ad44
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
10 """ifupdown2 network interface object
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
18 from collections
import OrderedDict
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
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)
44 class ifaceLinkKind():
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.
56 BATMAN_ADV
= 0x0100000
57 # to indicate logical interface created by an external entity.
58 # the 'kind' of which ifupdown2 does not really understand
62 def to_str(cls
, kind
):
63 if kind
== cls
.BRIDGE
:
65 elif kind
== cls
.BOND
:
67 elif kind
== cls
.VLAN
:
69 elif kind
== cls
.VXLAN
:
74 class ifaceLinkPrivFlags():
75 """ This corresponds to kernel netdev->priv_flags
76 and can be BRIDGE_PORT, BOND_SLAVE etc """
81 BRIDGE_VLAN_AWARE
= 0x01000
82 BRIDGE_VXLAN
= 0x10000
83 ADDRESS_VIRTUAL_SLAVE
= 0x100000
85 KEEP_LINK_DOWN
= 0x10000000
88 def get_str(cls
, flag
):
89 if flag
== cls
.UNKNOWN
:
91 elif flag
== cls
.BRIDGE_PORT
:
93 elif flag
== cls
.BOND_SLAVE
:
95 elif flag
== cls
.VRF_SLAVE
:
97 elif flag
== cls
.BRIDGE_VLAN_AWARE
:
98 return 'vlan aware bridge'
99 elif flag
== cls
.BRIDGE_VXLAN
:
100 return 'vxlan bridge'
103 def get_all_str(cls
, flags
):
105 if flags
& cls
.BRIDGE_PORT
:
107 if flags
& cls
.BOND_SLAVE
:
109 if flags
& cls
.VRF_SLAVE
:
111 if flags
& cls
.BRIDGE_VLAN_AWARE
:
112 str += 'vlanawarebridge '
113 if flags
& cls
.BRIDGE_VXLAN
:
114 str += 'vxlanbridge '
117 class ifaceLinkType():
123 class VlanProtocols():
124 # Picked ID values from
125 # http://www.microhowto.info/howto/configure_an_ethernet_interface_as_a_qinq_vlan_trunk.html
128 '802.1AD' : '0x88a8',
132 '0x88a8' : '802.1AD',
135 class ifaceDependencyType():
136 """ Indicates type of dependency.
138 This class enumerates types of dependency relationships
141 iface dependency relationships can be classified
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.
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.
166 """Enumerates iface status """
175 def to_str(cls
, state
):
176 if state
== cls
.UNKNOWN
:
178 elif state
== cls
.SUCCESS
:
180 elif state
== cls
.ERROR
:
182 elif state
== cls
.NOTFOUND
:
186 def from_str(cls
, state_str
):
187 if state_str
== 'unknown':
189 elif state_str
== 'success':
191 elif state_str
== 'error':
195 """Enumerates iface state """
207 QUERY_CHECKCURR
= 0x9
211 def to_str(cls
, state
):
212 if state
== cls
.UNKNOWN
:
214 elif state
== cls
.NEW
:
216 elif state
== cls
.PRE_UP
:
218 elif state
== cls
.UP
:
220 elif state
== cls
.POST_UP
:
222 elif state
== cls
.PRE_DOWN
:
224 elif state
== cls
.DOWN
:
226 elif state
== cls
.POST_DOWN
:
228 elif state
== cls
.QUERY_CHECKCURR
:
229 return 'query-checkcurr'
230 elif state
== cls
.QUERY_RUNNING
:
231 return 'query-running'
234 def from_str(cls
, state_str
):
235 if state_str
== 'unknown':
237 elif state_str
== 'new':
239 elif state_str
== 'pre-up':
241 elif state_str
== 'up':
243 elif state_str
== 'post-up':
245 elif state_str
== 'pre-down':
247 elif state_str
== 'down':
249 elif state_str
== 'post-down':
251 elif state_str
== 'query-checkcurr':
252 return cls
.QUERY_CHECKCURR
253 elif state_str
== 'query-running':
254 return cls
.QUERY_RUNNING
256 class ifaceJsonEncoder(json
.JSONEncoder
):
257 def default(self
, o
):
259 retifacedict
= OrderedDict([])
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
265 if 'inet' in o
.addr_family
and 'dhcp' in o
.addr_method
:
266 retifacedict
['addr_method'] = 'dhcp'
268 retifacedict
['addr_method'] = o
.addr_method
270 if len(o
.addr_family
) > 1:
271 retifacedict
['addr_family'] = o
.addr_family
273 retifacedict
['addr_family'] = ' '.join(o
.addr_family
)
274 retifacedict
['auto'] = o
.auto
275 retifacedict
['config'] = retconfig
279 class ifaceJsonEncoderWithStatus(json
.JSONEncoder
):
280 def default(self
, o
):
282 retconfig_status
= {}
283 retifacedict
= OrderedDict([])
285 for k
,v
in o
.config
.items():
289 s
= o
.get_config_attr_status(k
, idx
)
291 status_str
= ifaceStatusUserStrs
.ERROR
293 status_str
= ifaceStatusUserStrs
.SUCCESS
295 status_str
= ifaceStatusUserStrs
.UNKNOWN
296 vitem_status
.append('%s' %status
_str
)
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
301 if (o
.status
== ifaceStatus
.NOTFOUND
or
302 o
.status
== ifaceStatus
.ERROR
):
303 status
= ifaceStatusUserStrs
.ERROR
305 status
= ifaceStatusUserStrs
.SUCCESS
307 retifacedict
['name'] = o
.name
309 retifacedict
['addr_method'] = o
.addr_method
311 if len(o
.addr_family
) > 1:
312 retifacedict
['addr_family'] = o
.addr_family
314 retifacedict
['addr_family'] = ' '.join(o
.addr_family
)
315 retifacedict
['auto'] = o
.auto
316 retifacedict
['config'] = retconfig
317 retifacedict
['config_status'] = retconfig_status
318 retifacedict
['status'] = status
322 class ifaceJsonDecoder():
324 def json_to_ifaceobj(cls
, ifaceattrdict
):
325 ifaceattrdict
['config'] = OrderedDict([(k
, (v
if isinstance(v
, list)
327 for k
,v
in ifaceattrdict
.get('config',
328 OrderedDict()).items()])
329 return iface(attrsdict
=ifaceattrdict
)
332 """ ifupdown2 iface object class
334 **name** Name of the interface
336 **addr_family** Address family eg, inet, inet6. Can be None to
337 indicate both address families
339 **addr_method** Address method eg, static, manual or None for
340 static address method
342 **config** dictionary of config lines for this interface
344 **state** Configuration state of an interface as defined by
347 **status** Configuration status of an interface as defined by
350 **flags** Internal flags used by iface processing
352 **priv_flags** private flags owned by module using this class
354 **module_flags** module flags owned by module using this class
356 **refcnt** reference count, indicating number of interfaces
357 dependent on this iface
359 **lowerifaces** list of interface names lower to this interface or
360 this interface depends on
362 **upperifaces** list of interface names upper to this interface or
363 the interfaces that depend on this interface
365 **auto** True if interface belongs to the auto class
367 **classes** List of classes the interface belongs to
369 **env** shell environment the interface needs during
372 **raw_config** raw interface config from file
375 # flag to indicate that the object was created from pickled state
376 # XXX: Move these flags into a separate iface flags class
377 _PICKLED
= 0x00000001
378 HAS_SIBLINGS
= 0x00000010
379 IFACERANGE_ENTRY
= 0x00000100
380 IFACERANGE_START
= 0x00001000
381 OLDEST_SIBLING
= 0x00010000
382 YOUNGEST_SIBLING
= 0x00100000
386 def __init__(self
, attrsdict
={}):
387 self
.addr_family
= []
389 self
._set
_attrs
_from
_dict
(attrsdict
)
390 self
._config
_status
= {}
391 """dict with config status of iface attributes"""
392 self
.state
= ifaceState
.NEW
393 """iface state (of type ifaceState) """
394 self
.status
= ifaceStatus
.UNKNOWN
395 """iface status (of type ifaceStatus) """
396 self
.status_str
= None
397 """iface status str (string representing the status) """
400 self
.priv_flags
= None
401 """iface module flags dictionary with module name: flags"""
402 self
.module_flags
= {}
403 """iface priv flags. can be used by the external object manager """
405 """iface refcnt (incremented for each dependent this interface has) """
406 self
.lowerifaces
= None
407 """lower iface list (in other words: slaves of this interface """
408 self
.upperifaces
= None
409 """upper iface list (in other words: master of this interface """
411 """interface classes this iface belongs to """
413 """environment variable dict required for this interface to run"""
415 """interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
416 self
.linkstate
= None
417 """linkstate of the interface"""
418 self
.type = ifaceType
.UNKNOWN
420 self
.priv_data
= None
421 self
.role
= ifaceRole
.UNKNOWN
423 self
.link_type
= ifaceLinkType
.LINK_UNKNOWN
424 self
.link_kind
= ifaceLinkKind
.UNKNOWN
425 self
.link_privflags
= ifaceLinkPrivFlags
.UNKNOWN
427 # The below attribute is used to disambiguate between various
428 # types of dependencies
429 self
.dependency_type
= ifaceDependencyType
.UNKNOWN
430 self
.blacklisted
= False
432 def _set_attrs_from_dict(self
, attrdict
):
433 self
.auto
= attrdict
.get('auto', False)
434 self
.name
= attrdict
.get('name')
435 self
.addr_method
= attrdict
.get('addr_method')
436 self
.config
= attrdict
.get('config', OrderedDict())
438 addr_family
= attrdict
.get('addr_family')
440 self
.addr_family
.append(addr_family
)
442 def inc_refcnt(self
):
443 """ increment refcnt of the interface. Usually used to indicate that
444 it has dependents """
447 def dec_refcnt(self
):
448 """ decrement refcnt of the interface. Usually used to indicate that
449 it has lost its dependent """
452 def is_config_present(self
):
453 """ returns true if the interface has user provided config,
455 addr_method
= self
.addr_method
456 if addr_method
and addr_method
in ['dhcp', 'dhcp6', 'loopback']:
463 def set_class(self
, classname
):
464 """ appends class to the interfaces class list """
465 self
.classes
.append(classname
)
467 def set_state_n_status(self
, state
, status
):
468 """ sets state and status of an interface """
470 if status
> self
.status
:
473 def set_status(self
, status
):
474 """ sets status of an interface """
475 if status
> self
.status
:
478 def set_flag(self
, flag
):
481 def clear_flag(self
, flag
):
484 def add_to_upperifaces(self
, upperifacename
):
485 """ add to the list of upperifaces """
487 if upperifacename
not in self
.upperifaces
:
488 self
.upperifaces
.append(upperifacename
)
490 self
.upperifaces
= [upperifacename
]
492 def add_to_lowerifaces(self
, lowerifacename
):
493 """ add to the list of lowerifaces """
495 if lowerifacename
not in self
.lowerifaces
:
496 self
.lowerifaces
.append(lowerifacename
)
498 self
.lowerifaces
= [lowerifacename
]
500 def get_attr_value(self
, attr_name
):
501 """ add to the list of upperifaces """
502 return self
.config
.get(attr_name
)
504 def get_attr_value_first(self
, attr_name
):
505 """ get first value of the specified attr name """
506 attr_value_list
= self
.config
.get(attr_name
)
508 return attr_value_list
[0]
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
517 attr_value_list
= self
.config
.get(attr
)
519 return attr_value_list
[0]
522 def get_attr_value_n(self
, attr_name
, attr_index
):
523 """ get n'th value of the specified attr name """
524 attr_value_list
= self
.config
.get(attr_name
)
527 return attr_value_list
[attr_index
]
533 """ get shell environment variables the interface must execute in """
538 def generate_env(self
):
539 """ generate shell environment variables dict interface must execute
540 in. This is used to support legacy ifupdown scripts
544 env
['IFACE'] = self
.name
545 for attr
, attr_value
in config
.items():
546 attr_env_name
= 'IF_%s' %attr
.upper().replace("-", "_")
547 env
[attr_env_name
] = attr_value
[0]
550 def update_config(self
, attr_name
, attr_value
):
551 """ add attribute name and value to the interface config """
552 self
.config
.setdefault(attr_name
, []).append(attr_value
)
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
]
558 def delete_config(self
, attr_name
):
559 """ add attribute name and value to the interface config """
561 del self
.config
[attr_name
]
565 def update_config_dict(self
, attrdict
):
566 self
.config
.update(attrdict
)
568 def update_config_with_status(self
, attr_name
, attr_value
, attr_status
=0):
569 """ add attribute name and value to the interface config and also
570 update the config_status dict with status of this attribute config """
573 self
.config
.setdefault(attr_name
, []).append(attr_value
)
574 self
._config
_status
.setdefault(attr_name
, []).append(attr_status
)
575 # set global iface state
577 self
.status
= ifaceStatus
.ERROR
578 elif self
.status
!= ifaceStatus
.ERROR
:
579 # Not already error, mark success
580 self
.status
= ifaceStatus
.SUCCESS
582 def check_n_update_config_with_status_many(self
, ifaceobjorig
, attr_names
,
584 # set multiple attribute status to zero
585 # also updates status only if the attribute is present
586 for attr_name
in attr_names
:
587 if not ifaceobjorig
.get_attr_value_first(attr_name
):
589 self
.config
.setdefault(attr_name
, []).append('')
590 self
._config
_status
.setdefault(attr_name
, []).append(attr_status
)
592 def get_config_attr_status(self
, attr_name
, idx
=0):
593 """ get status of a attribute config on this interface.
595 Looks at the iface _config_status dict"""
596 return self
._config
_status
.get(attr_name
, [])[idx
]
598 def compare(self
, dstiface
):
599 """ compares iface object with iface object passed as argument
601 Returns True if object self is same as dstiface and False otherwise """
603 if self
.name
!= dstiface
.name
: return False
604 if self
.type != dstiface
.type: return False
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
609 if len(self
.config
) != len(dstiface
.config
):
611 if any(True for k
in self
.config
if k
not in dstiface
.config
):
613 if any(True for k
,v
in self
.config
.items()
614 if v
!= dstiface
.config
.get(k
)): return False
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
)
626 self
.config
.update([(attrname
, attrlist
)])
627 # we now support inet and inet6 together
628 self
.addr_family
.extend(newifaceobj
.addr_family
)
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
:
634 def __getstate__(self
):
635 odict
= self
.__dict
__.copy()
638 del odict
['lowerifaces']
639 del odict
['upperifaces']
641 del odict
['_config_status']
643 del odict
['priv_flags']
644 del odict
['module_flags']
645 del odict
['raw_config']
646 del odict
['linkstate']
648 del odict
['link_type']
649 del odict
['link_kind']
650 del odict
['link_privflags']
652 del odict
['dependency_type']
653 del odict
['blacklisted']
656 def __setstate__(self
, dict):
657 self
.__dict
__.update(dict)
658 self
._config
_status
= {}
659 self
.state
= ifaceState
.NEW
660 self
.status
= ifaceStatus
.UNKNOWN
663 self
.lowerifaces
= None
664 self
.upperifaces
= None
665 self
.linkstate
= None
667 self
.role
= ifaceRole
.UNKNOWN
668 self
.priv_flags
= None
669 self
.module_flags
= {}
671 self
.flags |
= self
._PICKLED
672 self
.link_type
= ifaceLinkType
.LINK_NA
673 self
.link_kind
= ifaceLinkKind
.UNKNOWN
674 self
.link_privflags
= ifaceLinkPrivFlags
.UNKNOWN
675 self
.dependency_type
= ifaceDependencyType
.UNKNOWN
676 self
.blacklisted
= False
678 def dump_raw(self
, logger
):
681 print 'auto %s' %self
.name
682 print (self
.raw_config
[0])
683 for i
in range(1, len(self
.raw_config
)):
684 print(indent
+ self
.raw_config
[i
])
686 def dump(self
, logger
):
688 logger
.info(self
.name
+ ' : {')
689 logger
.info(indent
+ 'family: %s' % ' '.join(self
.addr_family
))
690 logger
.info(indent
+ 'method: %s' %self
.addr_method
)
691 logger
.info(indent
+ 'flags: %x' %self
.flags
)
692 logger
.info(indent
+ 'state: %s'
693 %ifaceState
.to_str(self
.state
))
694 logger
.info(indent
+ 'status: %s'
695 %ifaceStatus
.to_str(self
.status
))
696 logger
.info(indent
+ 'refcnt: %d' %self
.refcnt
)
699 logger
.info(indent
+ 'lowerdevs: %s' %str
(d
))
701 logger
.info(indent
+ 'lowerdevs: None')
705 logger
.info(indent
+ 'upperdevs: %s' %str
(d
))
707 logger
.info(indent
+ 'upperdevs: None')
709 logger
.info(indent
+ 'config: ')
712 logger
.info(indent
+ indent
+ str(config
))
715 def _dump_pretty(self
, family
, first
, addr_method
, with_status
=False, use_realname
=False):
718 if use_realname
and self
.realname
:
719 name
= '%s' %self
.realname
721 name
= '%s' %self
.name
723 outbuf
+= 'auto %s\n' %name
725 if self
.type == ifaceType
.BRIDGE_VLAN
:
726 ifaceline
+= 'vlan %s' %name
728 ifaceline
+= 'iface %s' %name
730 ifaceline
+= ' %s' % family
732 ifaceline
+= ' %s' % addr_method
735 if (self
.status
== ifaceStatus
.ERROR
or
736 self
.status
== ifaceStatus
.NOTFOUND
):
738 ifaceline
+= ' (%s)' %self
.status_str
739 status_str
= '[%s]' %ifaceStatusUserStrs
.ERROR
740 elif self
.status
== ifaceStatus
.SUCCESS
:
741 status_str
= '[%s]' %ifaceStatusUserStrs
.SUCCESS
743 outbuf
+= '{0:65} {1:>8}'.format(ifaceline
, status_str
) + '\n'
745 outbuf
+= ifaceline
+ '\n'
746 if self
.status
== ifaceStatus
.NOTFOUND
:
747 outbuf
= (outbuf
.encode('utf8')
748 if isinstance(outbuf
, unicode) else outbuf
)
752 outbuf
+= ifaceline
+ '\n'
755 for cname
, cvaluelist
in config
.items():
757 for cv
in cvaluelist
:
760 s
= self
.get_config_attr_status(cname
, idx
)
762 status_str
= '[%s]' %ifaceStatusUserStrs
.UNKNOWN
764 status_str
= '[%s]' %ifaceStatusUserStrs
.ERROR
766 status_str
= '[%s]' %ifaceStatusUserStrs
.SUCCESS
768 outbuf
+= (indent
+ '{0:55} {1:>10}'.format(
769 '%s %s' %(cname
, cv
), status_str
)) + '\n'
771 outbuf
+= indent
+ '%s %s\n' %(cname
, cv
)
774 outbuf
= (outbuf
.encode('utf8')
775 if isinstance(outbuf
, unicode) else outbuf
)
778 def dump_pretty(self
, with_status
=False, use_realname
=False):
779 if not self
.addr_family
:
780 self
._dump
_pretty
(None, True,
782 with_status
=with_status
,
783 use_realname
=use_realname
)
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:
793 # iface eth0 inet dhcp
797 # iface eth0 inet6 dhcp
800 for family
in self
.addr_family
:
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
:
807 self
._dump
_pretty
(family
, first
,
808 addr_method
=addr_method
,
809 with_status
=with_status
,
810 use_realname
=use_realname
)