]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdown/iface.py
75cc37ab3862f69d150eb3d441cf69ce97ef48fa
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 # to indicate logical interface created by an external entity.
57 # the 'kind' of which ifupdown2 does not really understand
61 def to_str(cls
, kind
):
62 if kind
== cls
.BRIDGE
:
64 elif kind
== cls
.BOND
:
66 elif kind
== cls
.VLAN
:
68 elif kind
== cls
.VXLAN
:
73 class ifaceLinkPrivFlags():
74 """ This corresponds to kernel netdev->priv_flags
75 and can be BRIDGE_PORT, BOND_SLAVE etc """
80 BRIDGE_VLAN_AWARE
= 0x01000
81 BRIDGE_VXLAN
= 0x10000
82 ADDRESS_VIRTUAL_SLAVE
= 0x100000
84 KEEP_LINK_DOWN
= 0x10000000
87 def get_str(cls
, flag
):
88 if flag
== cls
.UNKNOWN
:
90 elif flag
== cls
.BRIDGE_PORT
:
92 elif flag
== cls
.BOND_SLAVE
:
94 elif flag
== cls
.VRF_SLAVE
:
96 elif flag
== cls
.BRIDGE_VLAN_AWARE
:
97 return 'vlan aware bridge'
98 elif flag
== cls
.BRIDGE_VXLAN
:
102 def get_all_str(cls
, flags
):
104 if flags
& cls
.BRIDGE_PORT
:
106 if flags
& cls
.BOND_SLAVE
:
108 if flags
& cls
.VRF_SLAVE
:
110 if flags
& cls
.BRIDGE_VLAN_AWARE
:
111 str += 'vlanawarebridge '
112 if flags
& cls
.BRIDGE_VXLAN
:
113 str += 'vxlanbridge '
116 class ifaceLinkType():
122 class VlanProtocols():
123 # Picked ID values from
124 # http://www.microhowto.info/howto/configure_an_ethernet_interface_as_a_qinq_vlan_trunk.html
127 '802.1AD' : '0x88a8',
131 '0x88a8' : '802.1AD',
134 class ifaceDependencyType():
135 """ Indicates type of dependency.
137 This class enumerates types of dependency relationships
140 iface dependency relationships can be classified
145 In a 'link' dependency relationship, dependency can be shared
146 between interfaces. example: swp1.100 and
147 swp1.200 can both have 'link' swp1. swp1 is also a dependency
148 of swp1.100 and swp1.200. As you can see dependency
149 swp1 is shared between swp1.100 and swp1.200.
151 In a master/slave relationship like bridge and
152 its ports: eg: bridge br0 and its ports swp1 and swp2.
153 dependency swp1 and swp2 cannot be shared with any other
154 interface with the same dependency relationship.
155 ie, swp1 and swp2 cannot be in a slave relationship
156 with another interface. Understanding the dependency type is
157 required for any semantic checks between dependencies.
165 """Enumerates iface status """
174 def to_str(cls
, state
):
175 if state
== cls
.UNKNOWN
:
177 elif state
== cls
.SUCCESS
:
179 elif state
== cls
.ERROR
:
181 elif state
== cls
.NOTFOUND
:
185 def from_str(cls
, state_str
):
186 if state_str
== 'unknown':
188 elif state_str
== 'success':
190 elif state_str
== 'error':
194 """Enumerates iface state """
206 QUERY_CHECKCURR
= 0x9
210 def to_str(cls
, state
):
211 if state
== cls
.UNKNOWN
:
213 elif state
== cls
.NEW
:
215 elif state
== cls
.PRE_UP
:
217 elif state
== cls
.UP
:
219 elif state
== cls
.POST_UP
:
221 elif state
== cls
.PRE_DOWN
:
223 elif state
== cls
.DOWN
:
225 elif state
== cls
.POST_DOWN
:
227 elif state
== cls
.QUERY_CHECKCURR
:
228 return 'query-checkcurr'
229 elif state
== cls
.QUERY_RUNNING
:
230 return 'query-running'
233 def from_str(cls
, state_str
):
234 if state_str
== 'unknown':
236 elif state_str
== 'new':
238 elif state_str
== 'pre-up':
240 elif state_str
== 'up':
242 elif state_str
== 'post-up':
244 elif state_str
== 'pre-down':
246 elif state_str
== 'down':
248 elif state_str
== 'post-down':
250 elif state_str
== 'query-checkcurr':
251 return cls
.QUERY_CHECKCURR
252 elif state_str
== 'query-running':
253 return cls
.QUERY_RUNNING
255 class ifaceJsonEncoder(json
.JSONEncoder
):
256 def default(self
, o
):
258 retifacedict
= OrderedDict([])
260 retconfig
= dict((k
, (v
[0] if len(v
) == 1 else v
))
261 for k
,v
in o
.config
.items())
262 retifacedict
['name'] = o
.name
264 if 'inet' in o
.addr_family
and 'dhcp' in o
.addr_method
:
265 retifacedict
['addr_method'] = 'dhcp'
267 retifacedict
['addr_method'] = o
.addr_method
269 if len(o
.addr_family
) > 1:
270 retifacedict
['addr_family'] = o
.addr_family
272 retifacedict
['addr_family'] = ' '.join(o
.addr_family
)
273 retifacedict
['auto'] = o
.auto
274 retifacedict
['config'] = retconfig
278 class ifaceJsonEncoderWithStatus(json
.JSONEncoder
):
279 def default(self
, o
):
281 retconfig_status
= {}
282 retifacedict
= OrderedDict([])
284 for k
,v
in o
.config
.items():
288 s
= o
.get_config_attr_status(k
, idx
)
290 status_str
= ifaceStatusUserStrs
.ERROR
292 status_str
= ifaceStatusUserStrs
.SUCCESS
294 status_str
= ifaceStatusUserStrs
.UNKNOWN
295 vitem_status
.append('%s' %status
_str
)
297 retconfig
[k
] = v
[0] if len(v
) == 1 else v
298 retconfig_status
[k
] = vitem_status
[0] if len(vitem_status
) == 1 else vitem_status
300 if (o
.status
== ifaceStatus
.NOTFOUND
or
301 o
.status
== ifaceStatus
.ERROR
):
302 status
= ifaceStatusUserStrs
.ERROR
304 status
= ifaceStatusUserStrs
.SUCCESS
306 retifacedict
['name'] = o
.name
308 retifacedict
['addr_method'] = o
.addr_method
310 if len(o
.addr_family
) > 1:
311 retifacedict
['addr_family'] = o
.addr_family
313 retifacedict
['addr_family'] = ' '.join(o
.addr_family
)
314 retifacedict
['auto'] = o
.auto
315 retifacedict
['config'] = retconfig
316 retifacedict
['config_status'] = retconfig_status
317 retifacedict
['status'] = status
321 class ifaceJsonDecoder():
323 def json_to_ifaceobj(cls
, ifaceattrdict
):
324 ifaceattrdict
['config'] = OrderedDict([(k
, (v
if isinstance(v
, list)
326 for k
,v
in ifaceattrdict
.get('config',
327 OrderedDict()).items()])
328 return iface(attrsdict
=ifaceattrdict
)
331 """ ifupdown2 iface object class
333 **name** Name of the interface
335 **addr_family** Address family eg, inet, inet6. Can be None to
336 indicate both address families
338 **addr_method** Address method eg, static, manual or None for
339 static address method
341 **config** dictionary of config lines for this interface
343 **state** Configuration state of an interface as defined by
346 **status** Configuration status of an interface as defined by
349 **flags** Internal flags used by iface processing
351 **priv_flags** private flags owned by module using this class
353 **module_flags** module flags owned by module using this class
355 **refcnt** reference count, indicating number of interfaces
356 dependent on this iface
358 **lowerifaces** list of interface names lower to this interface or
359 this interface depends on
361 **upperifaces** list of interface names upper to this interface or
362 the interfaces that depend on this interface
364 **auto** True if interface belongs to the auto class
366 **classes** List of classes the interface belongs to
368 **env** shell environment the interface needs during
371 **raw_config** raw interface config from file
374 # flag to indicate that the object was created from pickled state
375 # XXX: Move these flags into a separate iface flags class
376 _PICKLED
= 0x00000001
377 HAS_SIBLINGS
= 0x00000010
378 IFACERANGE_ENTRY
= 0x00000100
379 IFACERANGE_START
= 0x00001000
380 OLDEST_SIBLING
= 0x00010000
381 YOUNGEST_SIBLING
= 0x00100000
385 def __init__(self
, attrsdict
={}):
386 self
.addr_family
= []
388 self
._set
_attrs
_from
_dict
(attrsdict
)
389 self
._config
_status
= {}
390 """dict with config status of iface attributes"""
391 self
.state
= ifaceState
.NEW
392 """iface state (of type ifaceState) """
393 self
.status
= ifaceStatus
.UNKNOWN
394 """iface status (of type ifaceStatus) """
395 self
.status_str
= None
396 """iface status str (string representing the status) """
399 self
.priv_flags
= None
400 """iface module flags dictionary with module name: flags"""
401 self
.module_flags
= {}
402 """iface priv flags. can be used by the external object manager """
404 """iface refcnt (incremented for each dependent this interface has) """
405 self
.lowerifaces
= None
406 """lower iface list (in other words: slaves of this interface """
407 self
.upperifaces
= None
408 """upper iface list (in other words: master of this interface """
410 """interface classes this iface belongs to """
412 """environment variable dict required for this interface to run"""
414 """interface config/attributes in raw format (eg: as it appeared in the interfaces file)"""
415 self
.linkstate
= None
416 """linkstate of the interface"""
417 self
.type = ifaceType
.UNKNOWN
419 self
.priv_data
= None
420 self
.role
= ifaceRole
.UNKNOWN
422 self
.link_type
= ifaceLinkType
.LINK_UNKNOWN
423 self
.link_kind
= ifaceLinkKind
.UNKNOWN
424 self
.link_privflags
= ifaceLinkPrivFlags
.UNKNOWN
426 # The below attribute is used to disambiguate between various
427 # types of dependencies
428 self
.dependency_type
= ifaceDependencyType
.UNKNOWN
429 self
.blacklisted
= False
431 def _set_attrs_from_dict(self
, attrdict
):
432 self
.auto
= attrdict
.get('auto', False)
433 self
.name
= attrdict
.get('name')
434 self
.addr_method
= attrdict
.get('addr_method')
435 self
.config
= attrdict
.get('config', OrderedDict())
437 addr_family
= attrdict
.get('addr_family')
439 self
.addr_family
.append(addr_family
)
441 def inc_refcnt(self
):
442 """ increment refcnt of the interface. Usually used to indicate that
443 it has dependents """
446 def dec_refcnt(self
):
447 """ decrement refcnt of the interface. Usually used to indicate that
448 it has lost its dependent """
451 def is_config_present(self
):
452 """ returns true if the interface has user provided config,
454 addr_method
= self
.addr_method
455 if addr_method
and addr_method
in ['dhcp', 'dhcp6', 'loopback']:
462 def set_class(self
, classname
):
463 """ appends class to the interfaces class list """
464 self
.classes
.append(classname
)
466 def set_state_n_status(self
, state
, status
):
467 """ sets state and status of an interface """
469 if status
> self
.status
:
472 def set_status(self
, status
):
473 """ sets status of an interface """
474 if status
> self
.status
:
477 def set_flag(self
, flag
):
480 def clear_flag(self
, flag
):
483 def add_to_upperifaces(self
, upperifacename
):
484 """ add to the list of upperifaces """
486 if upperifacename
not in self
.upperifaces
:
487 self
.upperifaces
.append(upperifacename
)
489 self
.upperifaces
= [upperifacename
]
491 def add_to_lowerifaces(self
, lowerifacename
):
492 """ add to the list of lowerifaces """
494 if lowerifacename
not in self
.lowerifaces
:
495 self
.lowerifaces
.append(lowerifacename
)
497 self
.lowerifaces
= [lowerifacename
]
499 def get_attr_value(self
, attr_name
):
500 """ add to the list of upperifaces """
501 return self
.config
.get(attr_name
)
503 def get_attr_value_first(self
, attr_name
):
504 """ get first value of the specified attr name """
505 attr_value_list
= self
.config
.get(attr_name
)
507 return attr_value_list
[0]
510 def get_attrs_value_first(self
, attrs
):
511 """ get first value of the first attr in the list.
512 Useful when you have multiple attrs representing the
516 attr_value_list
= self
.config
.get(attr
)
518 return attr_value_list
[0]
521 def get_attr_value_n(self
, attr_name
, attr_index
):
522 """ get n'th value of the specified attr name """
523 attr_value_list
= self
.config
.get(attr_name
)
526 return attr_value_list
[attr_index
]
532 """ get shell environment variables the interface must execute in """
537 def generate_env(self
):
538 """ generate shell environment variables dict interface must execute
539 in. This is used to support legacy ifupdown scripts
543 env
['IFACE'] = self
.name
544 for attr
, attr_value
in config
.items():
545 attr_env_name
= 'IF_%s' %attr
.upper().replace("-", "_")
546 env
[attr_env_name
] = attr_value
[0]
549 def update_config(self
, attr_name
, attr_value
):
550 """ add attribute name and value to the interface config """
551 self
.config
.setdefault(attr_name
, []).append(attr_value
)
553 def replace_config(self
, attr_name
, attr_value
):
554 """ add attribute name and value to the interface config """
555 self
.config
[attr_name
] = [attr_value
]
557 def delete_config(self
, attr_name
):
558 """ add attribute name and value to the interface config """
560 del self
.config
[attr_name
]
564 def update_config_dict(self
, attrdict
):
565 self
.config
.update(attrdict
)
567 def update_config_with_status(self
, attr_name
, attr_value
, attr_status
=0):
568 """ add attribute name and value to the interface config and also
569 update the config_status dict with status of this attribute config """
572 self
.config
.setdefault(attr_name
, []).append(attr_value
)
573 self
._config
_status
.setdefault(attr_name
, []).append(attr_status
)
574 # set global iface state
576 self
.status
= ifaceStatus
.ERROR
577 elif self
.status
!= ifaceStatus
.ERROR
:
578 # Not already error, mark success
579 self
.status
= ifaceStatus
.SUCCESS
581 def check_n_update_config_with_status_many(self
, ifaceobjorig
, attr_names
,
583 # set multiple attribute status to zero
584 # also updates status only if the attribute is present
585 for attr_name
in attr_names
:
586 if not ifaceobjorig
.get_attr_value_first(attr_name
):
588 self
.config
.setdefault(attr_name
, []).append('')
589 self
._config
_status
.setdefault(attr_name
, []).append(attr_status
)
591 def get_config_attr_status(self
, attr_name
, idx
=0):
592 """ get status of a attribute config on this interface.
594 Looks at the iface _config_status dict"""
595 return self
._config
_status
.get(attr_name
, [])[idx
]
597 def compare(self
, dstiface
):
598 """ compares iface object with iface object passed as argument
600 Returns True if object self is same as dstiface and False otherwise """
602 if self
.name
!= dstiface
.name
: return False
603 if self
.type != dstiface
.type: return False
604 if self
.addr_family
!= dstiface
.addr_family
: return False
605 if self
.addr_method
!= dstiface
.addr_method
: return False
606 if self
.auto
!= dstiface
.auto
: return False
607 if self
.classes
!= dstiface
.classes
: return False
608 if len(self
.config
) != len(dstiface
.config
):
610 if any(True for k
in self
.config
if k
not in dstiface
.config
):
612 if any(True for k
,v
in self
.config
.items()
613 if v
!= dstiface
.config
.get(k
)): return False
616 def squash(self
, newifaceobj
):
617 """ This squashes the iface object """
618 for attrname
, attrlist
in newifaceobj
.config
.iteritems():
619 # if allready present add it to the list
620 # else add it to the end of the dictionary
621 # We need to maintain order.
622 if self
.config
.get(attrname
):
623 self
.config
[attrname
].extend(attrlist
)
625 self
.config
.update([(attrname
, attrlist
)])
626 # we now support inet and inet6 together
627 self
.addr_family
.extend(newifaceobj
.addr_family
)
628 # if auto %ifacename is not part of the first stanza
629 # we need to squash it
630 if not self
.auto
and newifaceobj
.auto
:
633 def __getstate__(self
):
634 odict
= self
.__dict
__.copy()
637 del odict
['lowerifaces']
638 del odict
['upperifaces']
640 del odict
['_config_status']
642 del odict
['priv_flags']
643 del odict
['module_flags']
644 del odict
['raw_config']
645 del odict
['linkstate']
647 del odict
['link_type']
648 del odict
['link_kind']
649 del odict
['link_privflags']
651 del odict
['dependency_type']
652 del odict
['blacklisted']
655 def __setstate__(self
, dict):
656 self
.__dict
__.update(dict)
657 self
._config
_status
= {}
658 self
.state
= ifaceState
.NEW
659 self
.status
= ifaceStatus
.UNKNOWN
662 self
.lowerifaces
= None
663 self
.upperifaces
= None
664 self
.linkstate
= None
666 self
.role
= ifaceRole
.UNKNOWN
667 self
.priv_flags
= None
668 self
.module_flags
= {}
670 self
.flags |
= self
._PICKLED
671 self
.link_type
= ifaceLinkType
.LINK_NA
672 self
.link_kind
= ifaceLinkKind
.UNKNOWN
673 self
.link_privflags
= ifaceLinkPrivFlags
.UNKNOWN
674 self
.dependency_type
= ifaceDependencyType
.UNKNOWN
675 self
.blacklisted
= False
677 def dump_raw(self
, logger
):
680 print 'auto %s' %self
.name
681 print (self
.raw_config
[0])
682 for i
in range(1, len(self
.raw_config
)):
683 print(indent
+ self
.raw_config
[i
])
685 def dump(self
, logger
):
687 logger
.info(self
.name
+ ' : {')
688 logger
.info(indent
+ 'family: %s' % ' '.join(self
.addr_family
))
689 logger
.info(indent
+ 'method: %s' %self
.addr_method
)
690 logger
.info(indent
+ 'flags: %x' %self
.flags
)
691 logger
.info(indent
+ 'state: %s'
692 %ifaceState
.to_str(self
.state
))
693 logger
.info(indent
+ 'status: %s'
694 %ifaceStatus
.to_str(self
.status
))
695 logger
.info(indent
+ 'refcnt: %d' %self
.refcnt
)
698 logger
.info(indent
+ 'lowerdevs: %s' %str
(d
))
700 logger
.info(indent
+ 'lowerdevs: None')
704 logger
.info(indent
+ 'upperdevs: %s' %str
(d
))
706 logger
.info(indent
+ 'upperdevs: None')
708 logger
.info(indent
+ 'config: ')
711 logger
.info(indent
+ indent
+ str(config
))
714 def _dump_pretty(self
, family
, first
, addr_method
, with_status
=False, use_realname
=False):
717 if use_realname
and self
.realname
:
718 name
= '%s' %self
.realname
720 name
= '%s' %self
.name
722 outbuf
+= 'auto %s\n' %name
724 if self
.type == ifaceType
.BRIDGE_VLAN
:
725 ifaceline
+= 'vlan %s' %name
727 ifaceline
+= 'iface %s' %name
729 ifaceline
+= ' %s' % family
731 ifaceline
+= ' %s' % addr_method
734 if (self
.status
== ifaceStatus
.ERROR
or
735 self
.status
== ifaceStatus
.NOTFOUND
):
737 ifaceline
+= ' (%s)' %self
.status_str
738 status_str
= '[%s]' %ifaceStatusUserStrs
.ERROR
739 elif self
.status
== ifaceStatus
.SUCCESS
:
740 status_str
= '[%s]' %ifaceStatusUserStrs
.SUCCESS
742 outbuf
+= '{0:65} {1:>8}'.format(ifaceline
, status_str
) + '\n'
744 outbuf
+= ifaceline
+ '\n'
745 if self
.status
== ifaceStatus
.NOTFOUND
:
746 outbuf
= (outbuf
.encode('utf8')
747 if isinstance(outbuf
, unicode) else outbuf
)
751 outbuf
+= ifaceline
+ '\n'
754 for cname
, cvaluelist
in config
.items():
756 for cv
in cvaluelist
:
759 s
= self
.get_config_attr_status(cname
, idx
)
761 status_str
= '[%s]' %ifaceStatusUserStrs
.UNKNOWN
763 status_str
= '[%s]' %ifaceStatusUserStrs
.ERROR
765 status_str
= '[%s]' %ifaceStatusUserStrs
.SUCCESS
767 outbuf
+= (indent
+ '{0:55} {1:>10}'.format(
768 '%s %s' %(cname
, cv
), status_str
)) + '\n'
770 outbuf
+= indent
+ '%s %s\n' %(cname
, cv
)
773 outbuf
= (outbuf
.encode('utf8')
774 if isinstance(outbuf
, unicode) else outbuf
)
777 def dump_pretty(self
, with_status
=False, use_realname
=False):
778 if not self
.addr_family
:
779 self
._dump
_pretty
(None, True,
781 with_status
=with_status
,
782 use_realname
=use_realname
)
784 # To allow both inet and inet6 on an interface we changed the
785 # addr_family attribute, it's now a list. Depending on how
786 # stanzas were squashed (and what command was used ie. ifquery -r)
787 # we want to dump the ifaceobj as usual but we will output an
788 # empty stanza for each additional addr_family. The config will
789 # only be displayed once, in the first stanza. Example:
792 # iface eth0 inet dhcp
796 # iface eth0 inet6 dhcp
799 for family
in self
.addr_family
:
800 addr_method
= self
.addr_method
801 # We need to make sure we display 'dhcp' for inet family.
802 # In some cases it might take the value 'dhcp6' even if it has
803 # both inet and inet6 addr_family
804 if addr_method
and family
== 'inet' and 'dhcp' in addr_method
:
806 self
._dump
_pretty
(family
, first
,
807 addr_method
=addr_method
,
808 with_status
=with_status
,
809 use_realname
=use_realname
)