3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
13 from ifupdown2
.ifupdown
.iface
import *
14 from ifupdown2
.ifupdown
.utils
import utils
16 import ifupdown2
.ifupdown
.exceptions
as exceptions
17 import ifupdown2
.ifupdown
.policymanager
as policymanager
18 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
20 from ifupdown
.iface
import *
21 from ifupdown
.utils
import utils
23 import ifupdown
.exceptions
as exceptions
24 import ifupdown
.policymanager
as policymanager
25 import ifupdown
.ifupdownflags
as ifupdownflags
28 class NotSupported(Exception):
31 class moduleBase(object):
32 """ Base class for ifupdown addon modules
34 Provides common infrastructure methods for all addon modules """
36 def __init__(self
, *args
, **kargs
):
37 self
.modulename
= self
.__class
__.__name
__
38 self
.logger
= logging
.getLogger('ifupdown.' + self
.modulename
)
40 # vrfs are a global concept and a vrf context can be applicable
41 # to all global vrf commands. Get the default vrf-exec-cmd-prefix
42 # here so that all modules can use it
43 self
.vrf_exec_cmd_prefix
= policymanager
.policymanager_api
.get_module_globals('vrf', attr
='vrf-exec-cmd-prefix')
45 # explanations are shown in parse_glob
46 self
.glob_regexs
= [re
.compile(r
"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\]([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)"),
47 re
.compile(r
"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
48 re
.compile(r
"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
50 self
._bridge
_stp
_user
_space
= None
52 self
.merge_modinfo_with_policy_files()
54 def merge_modinfo_with_policy_files(self
):
56 update addons modinfo dictionary with system/user defined values in policy files
57 Any value can be updated except the module help "mhelp"
59 We also check if the policy attributes really exist to make sure someone is not
60 trying to "inject" new attributes to prevent breakages and security issue
62 attrs
= dict(self
.get_modinfo().get('attrs', {}))
67 error_msg
= 'this attribute doesn\'t exist or isn\'t supported'
69 # first check module_defaults
70 for key
, value
in policymanager
.policymanager_api
.get_module_defaults(self
.modulename
).items():
72 self
.logger
.warning('%s: %s: %s' % (self
.modulename
, key
, error_msg
))
74 attrs
[key
]['default'] = value
76 # then check module_globals (overrides module_defaults)
77 policy_modinfo
= policymanager
.policymanager_api
.get_module_globals(self
.modulename
, '_modinfo')
79 policy_attrs
= policy_modinfo
.get('attrs', {})
82 for attr_name
, attr_description
in policy_attrs
.items():
83 if attr_name
not in attrs
:
84 self
.logger
.warning('%s: %s: %s' % (self
.modulename
, attr_name
, error_msg
))
86 update_attrs
[attr_name
] = attr_description
88 attrs
.update(update_attrs
)
92 def log_warn(self
, str, ifaceobj
=None):
93 """ log a warning if err str is not one of which we should ignore """
94 if not self
.ignore_error(str) and not ifupdownflags
.flags
.IGNORE_ERRORS
:
95 if self
.logger
.getEffectiveLevel() == logging
.DEBUG
:
96 traceback
.print_stack()
100 ifaceobj
.set_status(ifaceStatus
.WARNING
)
103 def log_error(self
, str, ifaceobj
=None, raise_error
=True):
104 """ log an err if err str is not one of which we should ignore and raise an exception """
105 if not self
.ignore_error(str) and not ifupdownflags
.flags
.IGNORE_ERRORS
:
106 if self
.logger
.getEffectiveLevel() == logging
.DEBUG
:
107 traceback
.print_stack()
108 traceback
.print_exc()
111 ifaceobj
.set_status(ifaceStatus
.ERROR
)
114 self
.logger
.error(str)
118 def is_process_running(self
, procName
):
120 utils
.exec_command('%s -x %s' %
121 (utils
.pidof_cmd
, procName
))
127 def get_ifaces_from_proc(self
):
129 with
open('/proc/net/dev') as f
:
131 lines
= f
.readlines()
132 for line
in lines
[2:]:
133 ifacenames
.append(line
.split()[0].strip(': '))
138 def parse_regex(self
, ifacename
, expr
, ifacenames
=None):
140 proc_ifacenames
= self
.get_ifaces_from_proc()
142 self
.logger
.warn('%s: error reading ifaces from proc' %ifacename
)
144 for proc_ifacename
in proc_ifacenames
:
146 if re
.search(expr
+ '$', proc_ifacename
):
149 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
150 %(ifacename
, expr
, proc_ifacename
, str(e
)))
153 for ifacename
in ifacenames
:
155 if re
.search(expr
+ '$', ifacename
):
158 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
159 %(ifacename
, expr
, ifacename
, str(e
)))
161 def ifname_is_glob(self
, ifname
):
163 Used by iface where ifname could be swp7 or swp[1-10].300
165 if (self
.glob_regexs
[0].match(ifname
) or
166 self
.glob_regexs
[1].match(ifname
) or
167 self
.glob_regexs
[2].match(ifname
)):
171 def parse_glob(self
, ifacename
, expr
):
172 errmsg
= ('error parsing glob expression \'%s\'' %expr
+
173 ' (supported glob syntax: swp1-10.300 or swp[1-10].300' +
174 ' or swp[1-10]sub[0-4].300')
177 self
.logger
.warn('%s: comma are not supported in glob: %s' % (ifacename
, errmsg
))
181 regexs
= self
.glob_regexs
183 if regexs
[0].match(expr
):
184 # the first regex checks for exactly two levels of ranges defined only with square brackets
185 # (e.g. swpxyz[10-23]subqwe[0-4].100) to handle naming with two levels of port names.
186 m
= regexs
[0].match(expr
)
189 # we have problems and should not continue
190 raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename
, expr
,errmsg
))
194 start_index
= int(mlist
[1])
195 end_index
= int(mlist
[2])
196 sub_string
= mlist
[3]
197 start_sub
= int(mlist
[4])
198 end_sub
= int(mlist
[5])
199 for i
in range(start_index
, end_index
+ 1):
200 for j
in range(start_sub
, end_sub
+ 1):
201 yield prefix
+ '%d%s%d' % (i
,sub_string
,j
) + suffix
203 elif regexs
[1].match(expr
) or regexs
[2].match(expr
):
204 # the second regex for 1 level with a range (e.g. swp10-14.100
205 # the third regex checks for 1 level with [] (e.g. swp[10-14].100)
208 if regexs
[1].match(expr
):
209 m
= regexs
[1].match(expr
)
211 m
= regexs
[2].match(expr
)
214 raise Exception('%s: ' %ifacename
+ errmsg
+ '(unexpected len)')
217 start_index
= int(mlist
[1])
218 end_index
= int(mlist
[2])
219 for i
in range(start_index
, end_index
+ 1):
220 yield prefix
+ '%d' %i + suffix
223 # Could not match anything.
224 self
.logger
.warn('%s: %s' %(ifacename
, errmsg
))
227 def parse_port_list(self
, ifacename
, port_expr
, ifacenames
=None):
228 """ parse port list containing glob and regex
231 port_expr (str): expression
232 ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
240 exprs
= re
.split(r
'[\s\t]\s*', port_expr
)
241 self
.logger
.debug('%s: evaluating port expr \'%s\''
242 %(ifacename
, str(exprs
)))
244 if expr
== 'noregex':
246 elif expr
== 'noglob':
248 elif expr
== 'regex':
253 for port
in self
.parse_regex(ifacename
, expr
, ifacenames
):
254 if port
not in portlist
:
255 portlist
.append(port
)
258 for port
in self
.parse_glob(ifacename
, expr
):
259 portlist
.append(port
)
262 portlist
.append(expr
)
267 def ignore_error(self
, errmsg
):
268 if (ifupdownflags
.flags
.FORCE
or re
.search(r
'exists', errmsg
,
269 re
.IGNORECASE | re
.MULTILINE
)):
273 def write_file(self
, filename
, strexpr
):
274 """ writes string to a file """
276 self
.logger
.info('writing \'%s\'' %strexpr
+
277 ' to file %s' %filename
)
278 if ifupdownflags
.flags
.DRYRUN
:
280 with
open(filename
, 'w') as f
:
283 self
.logger
.warn('error writing to file %s'
284 %filename
+ '(' + str(e
) + ')')
288 def read_file(self
, filename
):
289 """ read file and return lines from the file """
291 self
.logger
.info('reading \'%s\'' %filename
)
292 with
open(filename
, 'r') as f
:
298 def read_file_oneline(self
, filename
):
299 """ reads and returns first line from the file """
301 self
.logger
.info('reading \'%s\'' %filename
)
302 with
open(filename
, 'r') as f
:
303 return f
.readline().strip('\n')
308 def sysctl_set(self
, variable
, value
):
309 """ set sysctl variable to value passed as argument """
310 utils
.exec_command('%s %s=%s' %
311 (utils
.sysctl_cmd
, variable
, value
))
313 def sysctl_get(self
, variable
):
314 """ get value of sysctl variable """
315 output
= utils
.exec_command('%s %s' %
316 (utils
.sysctl_cmd
, variable
))
317 split
= output
.split('=')
319 return split
[1].strip()
322 def systcl_get_net_bridge_stp_user_space(self
):
323 if self
._bridge
_stp
_user
_space
:
324 return self
._bridge
_stp
_user
_space
326 self
._bridge
_stp
_user
_space
= self
.sysctl_get('net.bridge.bridge-stp-user-space')
328 self
._bridge
_stp
_user
_space
= 0
330 return self
._bridge
_stp
_user
_space
332 def set_iface_attr(self
, ifaceobj
, attr_name
, attr_valsetfunc
,
333 prehook
=None, prehookargs
=None):
334 ifacename
= ifaceobj
.name
335 attrvalue
= ifaceobj
.get_attr_value_first(attr_name
)
342 attr_valsetfunc(ifacename
, attrvalue
)
344 def query_n_update_ifaceobjcurr_attr(self
, ifaceobj
, ifaceobjcurr
,
345 attr_name
, attr_valgetfunc
,
346 attr_valgetextraarg
=None):
347 attrvalue
= ifaceobj
.get_attr_value_first(attr_name
)
350 if attr_valgetextraarg
:
351 runningattrvalue
= attr_valgetfunc(ifaceobj
.name
,
354 runningattrvalue
= attr_valgetfunc(ifaceobj
.name
)
355 if (not runningattrvalue
or
356 (runningattrvalue
!= attrvalue
)):
357 ifaceobjcurr
.update_config_with_status(attr_name
,
360 ifaceobjcurr
.update_config_with_status(attr_name
,
363 def dict_key_subset(self
, a
, b
):
364 """ returns a list of differing keys """
365 return [x
for x
in a
if x
in b
]
367 def get_mod_attrs(self
):
368 """ returns list of all module attrs defined in the module _modinfo
373 attrsdict
= self
._modinfo
.get('attrs')
374 for attrname
, attrvals
in attrsdict
.iteritems():
375 if not attrvals
or attrvals
.get('deprecated'):
377 retattrs
.append(attrname
)
378 if 'aliases' in attrvals
:
379 retattrs
.extend(attrvals
['aliases'])
384 def get_mod_attr(self
, attrname
):
385 """ returns module attr info """
387 return self
._modinfo
.get('attrs', {}).get(attrname
)
391 def get_mod_subattr(self
, attrname
, subattrname
):
392 """ returns module attrs defined in the module _modinfo dict"""
394 return reduce(lambda d
, k
: d
[k
], ['attrs', attrname
, subattrname
],
399 def get_modinfo(self
):
400 """ return module info """
406 def get_attr_default_value(self
, attrname
):
407 return self
.get_modinfo().get('attrs', {}).get(attrname
, {}).get('default')
409 def get_overrides_ifupdown_scripts(self
):
410 """ return the ifupdown scripts replaced by the current module """
412 return self
.overrides_ifupdown_scripts
416 def _get_reserved_vlan_range(self
):
418 get_resvvlan
= '/var/lib/ifupdown2/hooks/get_reserved_vlan_range.sh'
419 if not os
.path
.exists(get_resvvlan
):
422 (s
, e
) = utils
.exec_command(get_resvvlan
).strip('\n').split('-')
426 self
.logger
.debug('%s failed (%s)' %(get_resvvlan
, str(e
)))
431 def _handle_reserved_vlan(self
, vlanid
, logprefix
='', end
=-1):
432 """ Helper function to check and warn if the vlanid falls in the
433 reserved vlan range """
435 invalid_vlan
= vlanid
437 if self
._resv
_vlan
_range
[0] <= vlanid
<= self
._resv
_vlan
_range
[1]:
440 if self
._resv
_vlan
_range
[0] <= end
<= self
._resv
_vlan
_range
[1]:
443 elif vlanid
< self
._resv
_vlan
_range
[0] and end
> self
._resv
_vlan
_range
[1]:
445 invalid_vlan
= self
._resv
_vlan
_range
[0]
448 raise exceptions
.ReservedVlanException('%s: reserved vlan %d being used (reserved vlan range %d-%d)'
449 % (logprefix
, invalid_vlan
, self
._resv
_vlan
_range
[0], self
._resv
_vlan
_range
[1]))
453 def _valid_ethaddr(self
, ethaddr
):
454 """ Check if address is 00:00:00:00:00:00 """
455 if not ethaddr
or re
.match('00:00:00:00:00:00', ethaddr
):
459 def _get_vlan_id_from_ifacename(self
, ifacename
):
461 vid_str
= ifacename
.split('.', 2)
467 elif ifacename
.startswith('vlan'):
468 vid_str
= ifacename
[4:]
477 def _get_vlan_id(self
, ifaceobj
):
478 """ Derives vlanid from iface name
481 Returns 1 for ifname vlan0001 returns 1
482 Returns 1 for ifname vlan1
483 Returns 1 for ifname eth0.1
484 Returns 100 for ifname eth0.1.100
485 Returns -1 if vlan id cannot be determined
487 vid_str
= ifaceobj
.get_attr_value_first('vlan-id')
489 if vid_str
: return int(vid_str
)
493 return self
._get
_vlan
_id
_from
_ifacename
(ifaceobj
.name
)