3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
13 from ifupdown
.utils
import utils
14 from ifupdown
.iface
import *
15 import ifupdown
.policymanager
as policymanager
16 import ifupdown
.ifupdownflags
as ifupdownflags
18 class NotSupported(Exception):
21 class moduleBase(object):
22 """ Base class for ifupdown addon modules
24 Provides common infrastructure methods for all addon modules """
26 def __init__(self
, *args
, **kargs
):
27 modulename
= self
.__class
__.__name
__
28 self
.logger
= logging
.getLogger('ifupdown.' + modulename
)
30 # vrfs are a global concept and a vrf context can be applicable
31 # to all global vrf commands. Get the default vrf-exec-cmd-prefix
32 # here so that all modules can use it
33 self
.vrf_exec_cmd_prefix
= policymanager
.policymanager_api
.get_module_globals('vrf', attr
='vrf-exec-cmd-prefix')
35 # explanations are shown in parse_glob
36 self
.glob_regexs
= [re
.compile(r
"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\]([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)"),
37 re
.compile(r
"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
38 re
.compile(r
"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
40 self
._bridge
_stp
_user
_space
= None
43 def log_warn(self
, str, ifaceobj
=None):
44 """ log a warning if err str is not one of which we should ignore """
45 if not self
.ignore_error(str) and not ifupdownflags
.flags
.IGNORE_ERRORS
:
46 if self
.logger
.getEffectiveLevel() == logging
.DEBUG
:
47 traceback
.print_stack()
50 ifaceobj
.set_status(ifaceStatus
.WARNING
)
53 def log_error(self
, str, ifaceobj
=None, raise_error
=True):
54 """ log an err if err str is not one of which we should ignore and raise an exception """
55 if not self
.ignore_error(str) and not ifupdownflags
.flags
.IGNORE_ERRORS
:
56 if self
.logger
.getEffectiveLevel() == logging
.DEBUG
:
57 traceback
.print_stack()
60 ifaceobj
.set_status(ifaceStatus
.ERROR
)
63 self
.logger
.error(str)
67 def is_process_running(self
, procName
):
69 utils
.exec_command('/bin/pidof -x %s' % procName
)
75 def get_ifaces_from_proc(self
):
77 with
open('/proc/net/dev') as f
:
80 for line
in lines
[2:]:
81 ifacenames
.append(line
.split()[0].strip(': '))
86 def parse_regex(self
, ifacename
, expr
, ifacenames
=None):
88 proc_ifacenames
= self
.get_ifaces_from_proc()
90 self
.logger
.warn('%s: error reading ifaces from proc' %ifacename
)
92 for proc_ifacename
in proc_ifacenames
:
94 if re
.search(expr
+ '$', proc_ifacename
):
97 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
98 %(ifacename
, expr
, proc_ifacename
, str(e
)))
101 for ifacename
in ifacenames
:
103 if re
.search(expr
+ '$', ifacename
):
106 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
107 %(ifacename
, expr
, ifacename
, str(e
)))
109 def ifname_is_glob(self
, ifname
):
111 Used by iface where ifname could be swp7 or swp[1-10].300
113 if (self
.glob_regexs
[0].match(ifname
) or
114 self
.glob_regexs
[1].match(ifname
) or
115 self
.glob_regexs
[2].match(ifname
)):
119 def parse_glob(self
, ifacename
, expr
):
120 errmsg
= ('error parsing glob expression \'%s\'' %expr
+
121 ' (supported glob syntax: swp1-10.300 or swp[1-10].300' +
122 ' or swp[1-10]sub[0-4].300')
123 regexs
= self
.glob_regexs
125 if regexs
[0].match(expr
):
126 # the first regex checks for exactly two levels of ranges defined only with square brackets
127 # (e.g. swpxyz[10-23]subqwe[0-4].100) to handle naming with two levels of port names.
128 m
= regexs
[0].match(expr
)
131 # we have problems and should not continue
132 raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename
, expr
,errmsg
))
136 start_index
= int(mlist
[1])
137 end_index
= int(mlist
[2])
138 sub_string
= mlist
[3]
139 start_sub
= int(mlist
[4])
140 end_sub
= int(mlist
[5])
141 for i
in range(start_index
, end_index
+ 1):
142 for j
in range(start_sub
, end_sub
+ 1):
143 yield prefix
+ '%d%s%d' % (i
,sub_string
,j
) + suffix
145 elif regexs
[1].match(expr
) or regexs
[2].match(expr
):
146 # the second regex for 1 level with a range (e.g. swp10-14.100
147 # the third regex checks for 1 level with [] (e.g. swp[10-14].100)
150 if regexs
[1].match(expr
):
151 m
= regexs
[1].match(expr
)
153 m
= regexs
[2].match(expr
)
156 raise Exception('%s: ' %ifacename
+ errmsg
+ '(unexpected len)')
159 start_index
= int(mlist
[1])
160 end_index
= int(mlist
[2])
161 for i
in range(start_index
, end_index
+ 1):
162 yield prefix
+ '%d' %i + suffix
165 # Could not match anything.
166 self
.logger
.warn('%s: %s' %(ifacename
, errmsg
))
169 def parse_port_list(self
, ifacename
, port_expr
, ifacenames
=None):
170 """ parse port list containing glob and regex
173 port_expr (str): expression
174 ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
182 exprs
= re
.split(r
'[\s\t]\s*', port_expr
)
183 self
.logger
.debug('%s: evaluating port expr \'%s\''
184 %(ifacename
, str(exprs
)))
186 if expr
== 'noregex':
188 elif expr
== 'noglob':
190 elif expr
== 'regex':
195 for port
in self
.parse_regex(ifacename
, expr
, ifacenames
):
196 if port
not in portlist
:
197 portlist
.append(port
)
200 for port
in self
.parse_glob(ifacename
, expr
):
201 portlist
.append(port
)
204 portlist
.append(expr
)
209 def ignore_error(self
, errmsg
):
210 if (ifupdownflags
.flags
.FORCE
or re
.search(r
'exists', errmsg
,
211 re
.IGNORECASE | re
.MULTILINE
)):
215 def write_file(self
, filename
, strexpr
):
216 """ writes string to a file """
218 self
.logger
.info('writing \'%s\'' %strexpr
+
219 ' to file %s' %filename
)
220 if ifupdownflags
.flags
.DRYRUN
:
222 with
open(filename
, 'w') as f
:
225 self
.logger
.warn('error writing to file %s'
226 %filename
+ '(' + str(e
) + ')')
230 def read_file(self
, filename
):
231 """ read file and return lines from the file """
233 self
.logger
.info('reading \'%s\'' %filename
)
234 with
open(filename
, 'r') as f
:
240 def read_file_oneline(self
, filename
):
241 """ reads and returns first line from the file """
243 self
.logger
.info('reading \'%s\'' %filename
)
244 with
open(filename
, 'r') as f
:
245 return f
.readline().strip('\n')
250 def sysctl_set(self
, variable
, value
):
251 """ set sysctl variable to value passed as argument """
252 utils
.exec_command('sysctl %s=%s' % (variable
, value
))
254 def sysctl_get(self
, variable
):
255 """ get value of sysctl variable """
256 output
= utils
.exec_command('sysctl %s' % variable
)
257 split
= output
.split('=')
259 return split
[1].strip()
262 def systcl_get_net_bridge_stp_user_space(self
):
263 if self
._bridge
_stp
_user
_space
:
264 return self
._bridge
_stp
_user
_space
265 self
._bridge
_stp
_user
_space
= self
.sysctl_get('net.bridge.bridge-stp-user-space')
266 return self
._bridge
_stp
_user
_space
268 def set_iface_attr(self
, ifaceobj
, attr_name
, attr_valsetfunc
,
269 prehook
=None, prehookargs
=None):
270 ifacename
= ifaceobj
.name
271 attrvalue
= ifaceobj
.get_attr_value_first(attr_name
)
278 attr_valsetfunc(ifacename
, attrvalue
)
280 def query_n_update_ifaceobjcurr_attr(self
, ifaceobj
, ifaceobjcurr
,
281 attr_name
, attr_valgetfunc
,
282 attr_valgetextraarg
=None):
283 attrvalue
= ifaceobj
.get_attr_value_first(attr_name
)
286 if attr_valgetextraarg
:
287 runningattrvalue
= attr_valgetfunc(ifaceobj
.name
,
290 runningattrvalue
= attr_valgetfunc(ifaceobj
.name
)
291 if (not runningattrvalue
or
292 (runningattrvalue
!= attrvalue
)):
293 ifaceobjcurr
.update_config_with_status(attr_name
,
296 ifaceobjcurr
.update_config_with_status(attr_name
,
299 def dict_key_subset(self
, a
, b
):
300 """ returns a list of differing keys """
301 return [x
for x
in a
if x
in b
]
303 def get_mod_attrs(self
):
304 """ returns list of all module attrs defined in the module _modinfo
309 attrsdict
= self
._modinfo
.get('attrs')
310 for attrname
, attrvals
in attrsdict
.iteritems():
311 if not attrvals
or attrvals
.get('deprecated'):
313 retattrs
.append(attrname
)
314 if 'aliases' in attrvals
:
315 retattrs
.extend(attrvals
['aliases'])
320 def get_mod_attr(self
, attrname
):
321 """ returns module attr info """
323 return self
._modinfo
.get('attrs', {}).get(attrname
)
327 def get_mod_subattr(self
, attrname
, subattrname
):
328 """ returns module attrs defined in the module _modinfo dict"""
330 return reduce(lambda d
, k
: d
[k
], ['attrs', attrname
, subattrname
],
335 def get_modinfo(self
):
336 """ return module info """
342 def get_overrides_ifupdown_scripts(self
):
343 """ return the ifupdown scripts replaced by the current module """
345 return self
.overrides_ifupdown_scripts
349 def _get_reserved_vlan_range(self
):
351 get_resvvlan
= '/var/lib/ifupdown2/hooks/get_reserved_vlan_range.sh'
352 if not os
.path
.exists(get_resvvlan
):
355 (s
, e
) = utils
.exec_command(get_resvvlan
).strip('\n').split('-')
359 self
.logger
.debug('%s failed (%s)' %(get_resvvlan
, str(e
)))
364 def _handle_reserved_vlan(self
, vlanid
, logprefix
=''):
365 """ Helper function to check and warn if the vlanid falls in the
366 reserved vlan range """
367 if vlanid
in range(self
._resv
_vlan
_range
[0],
368 self
._resv
_vlan
_range
[1]):
369 self
.logger
.error('%s: reserved vlan %d being used'
370 %(logprefix
, vlanid
) + ' (reserved vlan range %d-%d)'
371 %(self
._resv
_vlan
_range
[0], self
._resv
_vlan
_range
[1]))
375 def _valid_ethaddr(self
, ethaddr
):
376 """ Check if address is 00:00:00:00:00:00 """
377 if not ethaddr
or re
.match('00:00:00:00:00:00', ethaddr
):