]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdownaddons/modulebase.py
ifupdownaddons: bridgeutils: build cache only if bridge-utils installed
[mirror_ifupdown2.git] / ifupdownaddons / modulebase.py
CommitLineData
15ef32ea
RP
1#!/usr/bin/python
2#
3# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
19f8bb0b 7import os
15ef32ea
RP
8import re
9import io
10import logging
15ef32ea 11import traceback
a4a53f4b
JF
12
13from ifupdown.utils import utils
15ef32ea 14from ifupdown.iface import *
717cee31 15import ifupdown.policymanager as policymanager
fc5e1735 16import ifupdown.ifupdownflags as ifupdownflags
15ef32ea
RP
17
18class moduleBase(object):
19 """ Base class for ifupdown addon modules
20
21 Provides common infrastructure methods for all addon modules """
22
23 def __init__(self, *args, **kargs):
24 modulename = self.__class__.__name__
25 self.logger = logging.getLogger('ifupdown.' + modulename)
15ef32ea 26
717cee31
RP
27 # vrfs are a global concept and a vrf context can be applicable
28 # to all global vrf commands. Get the default vrf-exec-cmd-prefix
29 # here so that all modules can use it
30 self.vrf_exec_cmd_prefix = policymanager.policymanager_api.get_module_globals('vrf', attr='vrf-exec-cmd-prefix')
31
c6370b56
DW
32 # explanations are shown in parse_glob
33 self.glob_regexs = [re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\]([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)"),
34 re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
35 re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
36
37
b3745e05 38 def log_warn(self, str, ifaceobj=None):
15ef32ea
RP
39 """ log a warning if err str is not one of which we should ignore """
40 if not self.ignore_error(str):
41 if self.logger.getEffectiveLevel() == logging.DEBUG:
42 traceback.print_stack()
43 self.logger.warn(str)
b3745e05
RP
44 if ifaceobj:
45 ifaceobj.set_status(ifaceStatus.WARNING)
15ef32ea
RP
46 pass
47
bf3eda91 48 def log_error(self, str, ifaceobj=None, raise_error=True):
15ef32ea
RP
49 """ log an err if err str is not one of which we should ignore and raise an exception """
50 if not self.ignore_error(str):
51 if self.logger.getEffectiveLevel() == logging.DEBUG:
52 traceback.print_stack()
b3745e05
RP
53 if ifaceobj:
54 ifaceobj.set_status(ifaceStatus.ERROR)
bf3eda91
RP
55 if raise_error:
56 raise Exception(str)
15ef32ea
RP
57 else:
58 pass
59
fff589ea 60 def is_process_running(self, procName):
61 try:
a193d8d1 62 utils.exec_command('/bin/pidof -x %s' % procName)
fff589ea 63 except:
64 return False
65 else:
66 return True
67
15ef32ea
RP
68 def get_ifaces_from_proc(self):
69 ifacenames = []
70 with open('/proc/net/dev') as f:
71 try:
72 lines = f.readlines()
0c8332bc 73 for line in lines[2:]:
15ef32ea
RP
74 ifacenames.append(line.split()[0].strip(': '))
75 except:
76 raise
77 return ifacenames
78
0c8332bc 79 def parse_regex(self, ifacename, expr, ifacenames=None):
15ef32ea
RP
80 try:
81 proc_ifacenames = self.get_ifaces_from_proc()
82 except:
0c8332bc
RP
83 self.logger.warn('%s: error reading ifaces from proc' %ifacename)
84
15ef32ea 85 for proc_ifacename in proc_ifacenames:
0c8332bc
RP
86 try:
87 if re.search(expr + '$', proc_ifacename):
88 yield proc_ifacename
89 except Exception, e:
90 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
91 %(ifacename, expr, proc_ifacename, str(e)))
15ef32ea
RP
92 if not ifacenames:
93 return
94 for ifacename in ifacenames:
0c8332bc
RP
95 try:
96 if re.search(expr + '$', ifacename):
97 yield ifacename
98 except Exception, e:
99 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
100 %(ifacename, expr, ifacename, str(e)))
15ef32ea 101
c6370b56
DW
102 def ifname_is_glob(self, ifname):
103 """
104 Used by iface where ifname could be swp7 or swp[1-10].300
105 """
106 if (self.glob_regexs[0].match(ifname) or
107 self.glob_regexs[1].match(ifname) or
108 self.glob_regexs[2].match(ifname)):
109 return True
110 return False
111
0c8332bc 112 def parse_glob(self, ifacename, expr):
15ef32ea 113 errmsg = ('error parsing glob expression \'%s\'' %expr +
139662ee
ST
114 ' (supported glob syntax: swp1-10.300 or swp[1-10].300' +
115 ' or swp[1-10]sub[0-4].300')
c6370b56 116 regexs = self.glob_regexs
139662ee
ST
117
118 if regexs[0].match(expr):
119 # the first regex checks for exactly two levels of ranges defined only with square brackets
120 # (e.g. swpxyz[10-23]subqwe[0-4].100) to handle naming with two levels of port names.
121 m = regexs[0].match(expr)
122 mlist = m.groups()
123 if len(mlist) < 7:
124 # we have problems and should not continue
0c8332bc 125 raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename, expr,errmsg))
139662ee
ST
126
127 prefix = mlist[0]
128 suffix = mlist[6]
129 start_index = int(mlist[1])
130 end_index = int(mlist[2])
131 sub_string = mlist[3]
132 start_sub = int(mlist[4])
133 end_sub = int(mlist[5])
134 for i in range(start_index, end_index + 1):
135 for j in range(start_sub, end_sub + 1):
136 yield prefix + '%d%s%d' % (i,sub_string,j) + suffix
137
138 elif regexs[1].match(expr) or regexs[2].match(expr):
139 # the second regex for 1 level with a range (e.g. swp10-14.100
140 # the third regex checks for 1 level with [] (e.g. swp[10-14].100)
141 start_index = 0
142 end_index = 0
143 if regexs[1].match(expr):
144 m = regexs[1].match(expr)
145 else:
146 m = regexs[2].match(expr)
147 mlist = m.groups()
148 if len(mlist) != 4:
0c8332bc 149 raise Exception('%s: ' %ifacename + errmsg + '(unexpected len)')
139662ee
ST
150 prefix = mlist[0]
151 suffix = mlist[3]
152 start_index = int(mlist[1])
153 end_index = int(mlist[2])
15ef32ea
RP
154 for i in range(start_index, end_index + 1):
155 yield prefix + '%d' %i + suffix
156
139662ee
ST
157 else:
158 # Could not match anything.
0c8332bc 159 self.logger.warn('%s: %s' %(ifacename, errmsg))
139662ee
ST
160 yield expr
161
0c8332bc 162 def parse_port_list(self, ifacename, port_expr, ifacenames=None):
15ef32ea
RP
163 """ parse port list containing glob and regex
164
165 Args:
166 port_expr (str): expression
167 ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
168 """
169 regex = 0
170 glob = 0
171 portlist = []
172
173 if not port_expr:
174 return None
0c8332bc 175 exprs = re.split(r'[\s\t]\s*', port_expr)
77d9d664 176 self.logger.debug('%s: evaluating port expr \'%s\''
0c8332bc
RP
177 %(ifacename, str(exprs)))
178 for expr in exprs:
84ca006f
RP
179 if expr == 'noregex':
180 regex = 0
181 elif expr == 'noglob':
182 glob = 0
183 elif expr == 'regex':
15ef32ea
RP
184 regex = 1
185 elif expr == 'glob':
186 glob = 1
187 elif regex:
0c8332bc 188 for port in self.parse_regex(ifacename, expr, ifacenames):
15ef32ea
RP
189 if port not in portlist:
190 portlist.append(port)
191 regex = 0
192 elif glob:
9a8ad21a 193 for port in self.parse_glob(ifacename, expr):
15ef32ea
RP
194 portlist.append(port)
195 glob = 0
196 else:
197 portlist.append(expr)
198 if not portlist:
199 return None
200 return portlist
201
202 def ignore_error(self, errmsg):
fc5e1735 203 if (ifupdownflags.flags.FORCE or re.search(r'exists', errmsg,
15ef32ea
RP
204 re.IGNORECASE | re.MULTILINE)):
205 return True
206 return False
207
208 def write_file(self, filename, strexpr):
209 """ writes string to a file """
210 try:
211 self.logger.info('writing \'%s\'' %strexpr +
212 ' to file %s' %filename)
fc5e1735 213 if ifupdownflags.flags.DRYRUN:
15ef32ea
RP
214 return 0
215 with open(filename, 'w') as f:
216 f.write(strexpr)
217 except IOError, e:
218 self.logger.warn('error writing to file %s'
219 %filename + '(' + str(e) + ')')
220 return -1
221 return 0
222
223 def read_file(self, filename):
224 """ read file and return lines from the file """
225 try:
226 self.logger.info('reading \'%s\'' %filename)
227 with open(filename, 'r') as f:
228 return f.readlines()
229 except:
230 return None
231 return None
232
233 def read_file_oneline(self, filename):
234 """ reads and returns first line from the file """
235 try:
236 self.logger.info('reading \'%s\'' %filename)
237 with open(filename, 'r') as f:
238 return f.readline().strip('\n')
239 except:
240 return None
241 return None
242
243 def sysctl_set(self, variable, value):
244 """ set sysctl variable to value passed as argument """
a193d8d1 245 utils.exec_command('sysctl %s=%s' % (variable, value))
15ef32ea
RP
246
247 def sysctl_get(self, variable):
248 """ get value of sysctl variable """
a193d8d1 249 return utils.exec_command('sysctl %s' % variable).split('=')[1].strip()
15ef32ea
RP
250
251 def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc,
252 prehook=None, prehookargs=None):
253 ifacename = ifaceobj.name
254 attrvalue = ifaceobj.get_attr_value_first(attr_name)
255 if attrvalue:
256 if prehook:
257 if prehookargs:
258 prehook(prehookargs)
259 else:
260 prehook(ifacename)
261 attr_valsetfunc(ifacename, attrvalue)
262
263 def query_n_update_ifaceobjcurr_attr(self, ifaceobj, ifaceobjcurr,
264 attr_name, attr_valgetfunc,
265 attr_valgetextraarg=None):
266 attrvalue = ifaceobj.get_attr_value_first(attr_name)
267 if not attrvalue:
268 return
269 if attr_valgetextraarg:
270 runningattrvalue = attr_valgetfunc(ifaceobj.name,
271 attr_valgetextraarg)
272 else:
273 runningattrvalue = attr_valgetfunc(ifaceobj.name)
274 if (not runningattrvalue or
275 (runningattrvalue != attrvalue)):
276 ifaceobjcurr.update_config_with_status(attr_name,
277 runningattrvalue, 1)
278 else:
279 ifaceobjcurr.update_config_with_status(attr_name,
280 runningattrvalue, 0)
281
282 def dict_key_subset(self, a, b):
283 """ returns a list of differing keys """
284 return [x for x in a if x in b]
285
286 def get_mod_attrs(self):
1553a881
RP
287 """ returns list of all module attrs defined in the module _modinfo
288 dict
289 """
15ef32ea 290 try:
1553a881
RP
291 retattrs = []
292 attrsdict = self._modinfo.get('attrs')
293 for attrname, attrvals in attrsdict.iteritems():
294 if not attrvals or attrvals.get('deprecated'):
295 continue
296 retattrs.append(attrname)
297 return retattrs
15ef32ea
RP
298 except:
299 return None
300
301 def get_mod_attr(self, attrname):
302 """ returns module attr info """
303 try:
304 return self._modinfo.get('attrs', {}).get(attrname)
305 except:
306 return None
307
308 def get_mod_subattr(self, attrname, subattrname):
309 """ returns module attrs defined in the module _modinfo dict"""
310 try:
311 return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
312 self._modinfo)
313 except:
314 return None
315
316 def get_modinfo(self):
317 """ return module info """
318 try:
319 return self._modinfo
320 except:
321 return None
322
2da58137
RP
323 def _get_reserved_vlan_range(self):
324 start = end = 0
325 get_resvvlan = '/usr/share/python-ifupdown2/get_reserved_vlan_range.sh'
19f8bb0b
RP
326 if not os.path.exists(get_resvvlan):
327 return (start, end)
2da58137 328 try:
a193d8d1 329 (s, e) = utils.exec_command(get_resvvlan).strip('\n').split('-')
ad25e7bb
RP
330 start = int(s)
331 end = int(e)
19f8bb0b
RP
332 except Exception, e:
333 self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
2da58137
RP
334 # ignore errors
335 pass
336 return (start, end)
cd3059b8 337
2708f915 338 def _handle_reserved_vlan(self, vlanid, logprefix=''):
cd3059b8
RP
339 """ Helper function to check and warn if the vlanid falls in the
340 reserved vlan range """
341 if vlanid in range(self._resv_vlan_range[0],
342 self._resv_vlan_range[1]):
2708f915
RP
343 self.logger.error('%s: reserved vlan %d being used'
344 %(logprefix, vlanid) + ' (reserved vlan range %d-%d)'
345 %(self._resv_vlan_range[0], self._resv_vlan_range[1]))
cd3059b8
RP
346 return True
347 return False
5828d8c5
RP
348
349 def _valid_ethaddr(self, ethaddr):
350 """ Check if address is 00:00:00:00:00:00 """
351 if not ethaddr or re.match('00:00:00:00:00:00', ethaddr):
352 return False
353 return True