3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
8 from ipaddr
import IPNetwork
10 from ifupdown
.iface
import *
11 from ifupdownaddons
.modulebase
import moduleBase
12 from ifupdownaddons
.iproute2
import iproute2
13 from ifupdownaddons
.dhclient
import dhclient
14 except ImportError, e
:
15 raise ImportError (str(e
) + "- required module not found")
17 class address(moduleBase
):
18 """ ifupdown2 addon module to configure address, mtu, hwaddress, alias
19 (description) on an interface """
21 _modinfo
= {'mhelp' : 'address configuration module for interfaces',
24 {'help' : 'ipv4 or ipv6 addresses',
25 'example' : ['address 10.0.12.3/24',
26 'address 2000:1000:1000:1000:3::5/128']},
29 'example' : ['netmask 255.255.255.0'],
32 {'help': 'broadcast address',
33 'example' : ['broadcast 10.0.1.255']},
36 'example' : ['scope host']},
37 'preferred-lifetime' :
38 {'help': 'preferred lifetime',
39 'example' : ['preferred-lifetime forever',
40 'preferred-lifetime 10']},
42 {'help': 'default gateway',
43 'example' : ['gateway 255.255.255.0']},
45 { 'help': 'interface mtu',
46 'example' : ['mtu 1600'],
49 {'help' : 'hw address',
50 'example': ['hwaddress 44:38:39:00:27:b8']},
52 { 'help': 'description/alias',
53 'example' : ['alias testnetwork']},
55 { 'help': 'purge existing addresses. By default ' +
56 'any existing ip addresses on an interface are ' +
57 'purged to match persistant addresses in the ' +
58 'interfaces file. Set this attribute to \'no\'' +
59 'if you want to preserve existing addresses',
61 'example' : ['address-purge yes/no']}}}
63 def __init__(self
, *args
, **kargs
):
64 moduleBase
.__init
__(self
, *args
, **kargs
)
66 self
._bridge
_fdb
_query
_cache
= {}
68 def _address_valid(self
, addrs
):
71 if any(map(lambda a
: True if a
[:7] != '0.0.0.0'
76 def _process_bridge(self
, ifaceobj
, up
):
77 hwaddress
= ifaceobj
.get_attr_value_first('hwaddress')
78 addrs
= ifaceobj
.get_attr_value_first('address')
79 is_vlan_dev_on_vlan_aware_bridge
= False
80 is_bridge
= self
.ipcmd
.is_bridge(ifaceobj
.name
)
82 if '.' in ifaceobj
.name
:
83 (bridgename
, vlan
) = ifaceobj
.name
.split('.')
84 is_vlan_dev_on_vlan_aware_bridge
= self
.ipcmd
.bridge_is_vlan_aware(bridgename
)
85 if ((is_bridge
and not self
.ipcmd
.bridge_is_vlan_aware(ifaceobj
.name
))
86 or is_vlan_dev_on_vlan_aware_bridge
):
87 if self
._address
_valid
(addrs
):
89 self
.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj
.name
+
92 self
.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj
.name
+
94 if hwaddress
and is_vlan_dev_on_vlan_aware_bridge
:
96 self
.ipcmd
.bridge_fdb_add(bridgename
, hwaddress
, vlan
)
98 self
.ipcmd
.bridge_fdb_del(bridgename
, hwaddress
, vlan
)
100 def _inet_address_config(self
, ifaceobj
):
101 purge_addresses
= ifaceobj
.get_attr_value_first('address-purge')
102 if not purge_addresses
:
103 purge_addresses
= 'yes'
105 addrs
= ifaceobj
.get_attr_value('address')
107 if ifaceobj
.role
& ifaceRole
.SLAVE
:
108 # we must not configure an IP address if the interface is enslaved
109 self
.log_warn('interface %s is enslaved and cannot have an IP Address' % \
112 # If user address is not in CIDR notation, convert them to CIDR
113 for addr_index
in range(0, len(addrs
)):
114 addr
= addrs
[addr_index
]
116 newaddrs
.append(addr
)
118 netmask
= ifaceobj
.get_attr_value_n('netmask', addr_index
)
120 prefixlen
= IPNetwork('%s' %addr
+
121 '/%s' %netmask
).prefixlen
122 newaddrs
.append(addr
+ '/%s' %prefixlen
)
124 newaddrs
.append(addr
)
126 if (not self
.PERFMODE
and
127 not (ifaceobj
.flags
& iface
.HAS_SIBLINGS
) and
128 purge_addresses
== 'yes'):
129 # if perfmode is not set and also if iface has no sibling
130 # objects, purge addresses that are not present in the new
132 runningaddrs
= self
.ipcmd
.addr_get(ifaceobj
.name
, details
=False)
133 if newaddrs
== runningaddrs
:
136 # if primary address is not same, there is no need to keep any.
137 # reset all addresses
138 if (newaddrs
and runningaddrs
and
139 (newaddrs
[0] != runningaddrs
[0])):
140 self
.ipcmd
.del_addr_all(ifaceobj
.name
)
142 self
.ipcmd
.del_addr_all(ifaceobj
.name
, newaddrs
)
144 self
.log_warn(str(e
))
147 for addr_index
in range(0, len(newaddrs
)):
149 self
.ipcmd
.addr_add(ifaceobj
.name
, newaddrs
[addr_index
],
150 ifaceobj
.get_attr_value_n('broadcast', addr_index
),
151 ifaceobj
.get_attr_value_n('pointopoint',addr_index
),
152 ifaceobj
.get_attr_value_n('scope', addr_index
),
153 ifaceobj
.get_attr_value_n('preferred-lifetime', addr_index
))
155 self
.log_error(str(e
))
157 def _up(self
, ifaceobj
):
158 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
160 addr_method
= ifaceobj
.addr_method
162 # release any stale dhcp addresses if present
163 if (addr_method
!= "dhcp" and not self
.PERFMODE
and
164 not (ifaceobj
.flags
& iface
.HAS_SIBLINGS
)):
165 # if not running in perf mode and ifaceobj does not have
166 # any sibling iface objects, kill any stale dhclient
168 dhclientcmd
= dhclient()
169 if dhclient
.is_running(ifaceobj
.name
):
170 # release any dhcp leases
171 dhclientcmd
.release(ifaceobj
.name
)
172 elif dhclient
.is_running6(ifaceobj
.name
):
173 dhclientcmd
.release6(ifaceobj
.name
)
177 self
.ipcmd
.batch_start()
178 if addr_method
!= "dhcp":
179 self
._inet
_address
_config
(ifaceobj
)
180 mtu
= ifaceobj
.get_attr_value_first('mtu')
182 self
.ipcmd
.link_set(ifaceobj
.name
, 'mtu', mtu
)
183 alias
= ifaceobj
.get_attr_value_first('alias')
185 self
.ipcmd
.link_set_alias(ifaceobj
.name
, alias
)
186 hwaddress
= ifaceobj
.get_attr_value_first('hwaddress')
188 self
.ipcmd
.link_set(ifaceobj
.name
, 'address', hwaddress
)
189 self
.ipcmd
.batch_commit()
192 # Handle special things on a bridge
193 self
._process
_bridge
(ifaceobj
, True)
195 self
.log_warn('%s: %s' %(ifaceobj
.name
, str(e
)))
198 if addr_method
!= "dhcp":
199 self
.ipcmd
.route_add_gateway(ifaceobj
.name
,
200 ifaceobj
.get_attr_value_first('gateway'))
202 def _down(self
, ifaceobj
):
204 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
206 addr_method
= ifaceobj
.addr_method
207 if addr_method
!= "dhcp":
208 self
.ipcmd
.route_del_gateway(ifaceobj
.name
,
209 ifaceobj
.get_attr_value_first('gateway'),
210 ifaceobj
.get_attr_value_first('metric'))
211 self
.ipcmd
.del_addr_all(ifaceobj
.name
)
212 alias
= ifaceobj
.get_attr_value_first('alias')
214 self
.ipcmd
.link_set(ifaceobj
.name
, 'alias', "\'\'")
215 # XXX hwaddress reset cannot happen because we dont know last
218 # Handle special things on a bridge
219 self
._process
_bridge
(ifaceobj
, False)
221 self
.logger
.debug('%s : %s' %(ifaceobj
.name
, str(e
)))
224 def _get_iface_addresses(self
, ifaceobj
):
225 addrlist
= ifaceobj
.get_attr_value('address')
228 if not addrlist
: return None
229 for addrindex
in range(0, len(addrlist
)):
230 addr
= addrlist
[addrindex
]
231 netmask
= ifaceobj
.get_attr_value_n('netmask', addrindex
)
233 prefixlen
= IPNetwork('%s' %addr
+
234 '/%s' %netmask
).prefixlen
235 addr
= addr
+ '/%s' %prefixlen
236 outaddrlist
.append(addr
)
239 def _get_bridge_fdbs(self
, bridgename
, vlan
):
240 fdbs
= self
._bridge
_fdb
_query
_cache
.get(bridgename
)
242 fdbs
= self
.ipcmd
.bridge_fdb_show_dev(bridgename
)
245 self
._bridge
_fdb
_query
_cache
[bridgename
] = fdbs
246 return fdbs
.get(vlan
)
248 def _check_addresses_in_bridge(self
, ifaceobj
, hwaddress
):
249 """ If the device is a bridge, make sure the addresses
250 are in the bridge """
251 if '.' in ifaceobj
.name
:
252 (bridgename
, vlan
) = ifaceobj
.name
.split('.')
253 if self
.ipcmd
.bridge_is_vlan_aware(bridgename
):
254 fdb_addrs
= self
._get
_bridge
_fdbs
(bridgename
, vlan
)
255 if not fdb_addrs
or hwaddress
not in fdb_addrs
:
259 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
260 runningaddrsdict
= None
261 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
262 self
.logger
.debug('iface %s not found' %ifaceobj
.name
)
264 addr_method
= ifaceobj
.addr_method
265 self
.query_n_update_ifaceobjcurr_attr(ifaceobj
, ifaceobjcurr
,
266 'mtu', self
.ipcmd
.link_get_mtu
)
267 hwaddress
= ifaceobj
.get_attr_value_first('hwaddress')
269 rhwaddress
= self
.ipcmd
.link_get_hwaddress(ifaceobj
.name
)
270 if not rhwaddress
or rhwaddress
!= hwaddress
:
271 ifaceobjcurr
.update_config_with_status('hwaddress', rhwaddress
,
273 elif not self
._check
_addresses
_in
_bridge
(ifaceobj
, hwaddress
):
274 # XXX: hw address is not in bridge
275 ifaceobjcurr
.update_config_with_status('hwaddress', rhwaddress
,
277 ifaceobjcurr
.status_str
= 'bridge fdb error'
279 ifaceobjcurr
.update_config_with_status('hwaddress', rhwaddress
,
281 self
.query_n_update_ifaceobjcurr_attr(ifaceobj
, ifaceobjcurr
,
282 'alias', self
.ipcmd
.link_get_alias
)
284 if addr_method
== 'dhcp':
286 addrs
= self
._get
_iface
_addresses
(ifaceobj
)
287 runningaddrsdict
= self
.ipcmd
.addr_get(ifaceobj
.name
)
289 # Set ifaceobjcurr method and family
290 ifaceobjcurr
.addr_method
= ifaceobj
.addr_method
291 ifaceobjcurr
.addr_family
= ifaceobj
.addr_family
292 if not runningaddrsdict
and not addrs
:
294 runningaddrs
= runningaddrsdict
.keys() if runningaddrsdict
else []
295 if runningaddrs
!= addrs
:
296 runningaddrsset
= set(runningaddrs
) if runningaddrs
else set([])
297 addrsset
= set(addrs
) if addrs
else set([])
298 if (ifaceobj
.flags
& iface
.HAS_SIBLINGS
):
301 # only check for addresses present in running config
302 addrsdiff
= addrsset
.difference(runningaddrsset
)
304 if addr
in addrsdiff
:
305 ifaceobjcurr
.update_config_with_status('address',
308 ifaceobjcurr
.update_config_with_status('address',
311 addrsdiff
= addrsset
.symmetric_difference(runningaddrsset
)
312 for addr
in addrsset
.union(runningaddrsset
):
313 if addr
in addrsdiff
:
314 ifaceobjcurr
.update_config_with_status('address',
317 ifaceobjcurr
.update_config_with_status('address',
320 [ifaceobjcurr
.update_config_with_status('address',
321 addr
, 0) for addr
in addrs
]
322 #XXXX Check broadcast address, scope, etc
325 def _query_running(self
, ifaceobjrunning
):
326 if not self
.ipcmd
.link_exists(ifaceobjrunning
.name
):
327 self
.logger
.debug('iface %s not found' %ifaceobjrunning
.name
)
329 dhclientcmd
= dhclient()
330 if (dhclientcmd
.is_running(ifaceobjrunning
.name
) or
331 dhclientcmd
.is_running6(ifaceobjrunning
.name
)):
332 # If dhcp is configured on the interface, we skip it
334 isloopback
= self
.ipcmd
.link_isloopback(ifaceobjrunning
.name
)
336 default_addrs
= ['127.0.0.1/8', '::1/128']
337 ifaceobjrunning
.addr_family
= 'inet'
338 ifaceobjrunning
.addr_method
= 'loopback'
341 runningaddrsdict
= self
.ipcmd
.addr_get(ifaceobjrunning
.name
)
343 [ifaceobjrunning
.update_config('address', addr
)
344 for addr
, addrattrs
in runningaddrsdict
.items()
345 if addr
not in default_addrs
]
346 mtu
= self
.ipcmd
.link_get_mtu(ifaceobjrunning
.name
)
348 (ifaceobjrunning
.name
== 'lo' and mtu
!= '16436') or
349 (ifaceobjrunning
.name
!= 'lo' and
350 mtu
!= self
.get_mod_subattr('mtu', 'default'))):
351 ifaceobjrunning
.update_config('mtu', mtu
)
352 alias
= self
.ipcmd
.link_get_alias(ifaceobjrunning
.name
)
354 ifaceobjrunning
.update_config('alias', alias
)
356 _run_ops
= {'up' : _up
,
358 'query-checkcurr' : _query_check
,
359 'query-running' : _query_running
}
362 """ returns list of ops supported by this module """
363 return self
._run
_ops
.keys()
365 def _init_command_handlers(self
):
367 self
.ipcmd
= iproute2(**self
.get_flags())
369 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
370 """ run address configuration on the interface object passed as argument
373 **ifaceobj** (object): iface object
375 **operation** (str): any of 'up', 'down', 'query-checkcurr',
378 query_ifaceobj (object): query check ifaceobject. This is only
379 valid when op is 'query-checkcurr'. It is an object same as
380 ifaceobj, but contains running attribute values and its config
381 status. The modules can use it to return queried running state
382 of interfaces. status is success if the running state is same
383 as user required state in ifaceobj. error otherwise.
385 if ifaceobj
.type == ifaceType
.BRIDGE_VLAN
:
387 op_handler
= self
._run
_ops
.get(operation
)
390 self
._init
_command
_handlers
()
391 if operation
== 'query-checkcurr':
392 op_handler(self
, ifaceobj
, query_ifaceobj
)
394 op_handler(self
, ifaceobj
)