]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdownaddons/modulebase.py
Merge pull request #80 from BarbarossaTM/tunnel-fixes-master
[mirror_ifupdown2.git] / ifupdownaddons / modulebase.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import os
8 import re
9 import io
10 import logging
11 import traceback
12
13 from ifupdown.utils import utils
14 from ifupdown.iface import *
15 import ifupdown.policymanager as policymanager
16 import ifupdown.ifupdownflags as ifupdownflags
17
18 class NotSupported(Exception):
19 pass
20
21 class moduleBase(object):
22 """ Base class for ifupdown addon modules
23
24 Provides common infrastructure methods for all addon modules """
25
26 def __init__(self, *args, **kargs):
27 modulename = self.__class__.__name__
28 self.logger = logging.getLogger('ifupdown.' + modulename)
29
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')
34
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+)\](.*)")]
39
40 self._bridge_stp_user_space = None
41
42
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()
48 self.logger.warn(str)
49 if ifaceobj:
50 ifaceobj.set_status(ifaceStatus.WARNING)
51 pass
52
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()
58 if raise_error:
59 if ifaceobj:
60 ifaceobj.set_status(ifaceStatus.ERROR)
61 raise Exception(str)
62 else:
63 self.logger.error(str)
64 else:
65 pass
66
67 def is_process_running(self, procName):
68 try:
69 utils.exec_command('/bin/pidof -x %s' % procName)
70 except:
71 return False
72 else:
73 return True
74
75 def get_ifaces_from_proc(self):
76 ifacenames = []
77 with open('/proc/net/dev') as f:
78 try:
79 lines = f.readlines()
80 for line in lines[2:]:
81 ifacenames.append(line.split()[0].strip(': '))
82 except:
83 raise
84 return ifacenames
85
86 def parse_regex(self, ifacename, expr, ifacenames=None):
87 try:
88 proc_ifacenames = self.get_ifaces_from_proc()
89 except:
90 self.logger.warn('%s: error reading ifaces from proc' %ifacename)
91
92 for proc_ifacename in proc_ifacenames:
93 try:
94 if re.search(expr + '$', proc_ifacename):
95 yield proc_ifacename
96 except Exception, e:
97 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
98 %(ifacename, expr, proc_ifacename, str(e)))
99 if not ifacenames:
100 return
101 for ifacename in ifacenames:
102 try:
103 if re.search(expr + '$', ifacename):
104 yield ifacename
105 except Exception, e:
106 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
107 %(ifacename, expr, ifacename, str(e)))
108
109 def ifname_is_glob(self, ifname):
110 """
111 Used by iface where ifname could be swp7 or swp[1-10].300
112 """
113 if (self.glob_regexs[0].match(ifname) or
114 self.glob_regexs[1].match(ifname) or
115 self.glob_regexs[2].match(ifname)):
116 return True
117 return False
118
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
124
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)
129 mlist = m.groups()
130 if len(mlist) < 7:
131 # we have problems and should not continue
132 raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename, expr,errmsg))
133
134 prefix = mlist[0]
135 suffix = mlist[6]
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
144
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)
148 start_index = 0
149 end_index = 0
150 if regexs[1].match(expr):
151 m = regexs[1].match(expr)
152 else:
153 m = regexs[2].match(expr)
154 mlist = m.groups()
155 if len(mlist) != 4:
156 raise Exception('%s: ' %ifacename + errmsg + '(unexpected len)')
157 prefix = mlist[0]
158 suffix = mlist[3]
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
163
164 else:
165 # Could not match anything.
166 self.logger.warn('%s: %s' %(ifacename, errmsg))
167 yield expr
168
169 def parse_port_list(self, ifacename, port_expr, ifacenames=None):
170 """ parse port list containing glob and regex
171
172 Args:
173 port_expr (str): expression
174 ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
175 """
176 regex = 0
177 glob = 0
178 portlist = []
179
180 if not port_expr:
181 return None
182 exprs = re.split(r'[\s\t]\s*', port_expr)
183 self.logger.debug('%s: evaluating port expr \'%s\''
184 %(ifacename, str(exprs)))
185 for expr in exprs:
186 if expr == 'noregex':
187 regex = 0
188 elif expr == 'noglob':
189 glob = 0
190 elif expr == 'regex':
191 regex = 1
192 elif expr == 'glob':
193 glob = 1
194 elif regex:
195 for port in self.parse_regex(ifacename, expr, ifacenames):
196 if port not in portlist:
197 portlist.append(port)
198 regex = 0
199 elif glob:
200 for port in self.parse_glob(ifacename, expr):
201 portlist.append(port)
202 glob = 0
203 else:
204 portlist.append(expr)
205 if not portlist:
206 return None
207 return portlist
208
209 def ignore_error(self, errmsg):
210 if (ifupdownflags.flags.FORCE or re.search(r'exists', errmsg,
211 re.IGNORECASE | re.MULTILINE)):
212 return True
213 return False
214
215 def write_file(self, filename, strexpr):
216 """ writes string to a file """
217 try:
218 self.logger.info('writing \'%s\'' %strexpr +
219 ' to file %s' %filename)
220 if ifupdownflags.flags.DRYRUN:
221 return 0
222 with open(filename, 'w') as f:
223 f.write(strexpr)
224 except IOError, e:
225 self.logger.warn('error writing to file %s'
226 %filename + '(' + str(e) + ')')
227 return -1
228 return 0
229
230 def read_file(self, filename):
231 """ read file and return lines from the file """
232 try:
233 self.logger.info('reading \'%s\'' %filename)
234 with open(filename, 'r') as f:
235 return f.readlines()
236 except:
237 return None
238 return None
239
240 def read_file_oneline(self, filename):
241 """ reads and returns first line from the file """
242 try:
243 self.logger.info('reading \'%s\'' %filename)
244 with open(filename, 'r') as f:
245 return f.readline().strip('\n')
246 except:
247 return None
248 return None
249
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))
253
254 def sysctl_get(self, variable):
255 """ get value of sysctl variable """
256 output = utils.exec_command('sysctl %s' % variable)
257 split = output.split('=')
258 if len(split) > 1:
259 return split[1].strip()
260 return None
261
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
267
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)
272 if attrvalue:
273 if prehook:
274 if prehookargs:
275 prehook(prehookargs)
276 else:
277 prehook(ifacename)
278 attr_valsetfunc(ifacename, attrvalue)
279
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)
284 if not attrvalue:
285 return
286 if attr_valgetextraarg:
287 runningattrvalue = attr_valgetfunc(ifaceobj.name,
288 attr_valgetextraarg)
289 else:
290 runningattrvalue = attr_valgetfunc(ifaceobj.name)
291 if (not runningattrvalue or
292 (runningattrvalue != attrvalue)):
293 ifaceobjcurr.update_config_with_status(attr_name,
294 runningattrvalue, 1)
295 else:
296 ifaceobjcurr.update_config_with_status(attr_name,
297 runningattrvalue, 0)
298
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]
302
303 def get_mod_attrs(self):
304 """ returns list of all module attrs defined in the module _modinfo
305 dict
306 """
307 try:
308 retattrs = []
309 attrsdict = self._modinfo.get('attrs')
310 for attrname, attrvals in attrsdict.iteritems():
311 if not attrvals or attrvals.get('deprecated'):
312 continue
313 retattrs.append(attrname)
314 if 'aliases' in attrvals:
315 retattrs.extend(attrvals['aliases'])
316 return retattrs
317 except:
318 return None
319
320 def get_mod_attr(self, attrname):
321 """ returns module attr info """
322 try:
323 return self._modinfo.get('attrs', {}).get(attrname)
324 except:
325 return None
326
327 def get_mod_subattr(self, attrname, subattrname):
328 """ returns module attrs defined in the module _modinfo dict"""
329 try:
330 return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
331 self._modinfo)
332 except:
333 return None
334
335 def get_modinfo(self):
336 """ return module info """
337 try:
338 return self._modinfo
339 except:
340 return None
341
342 def get_overrides_ifupdown_scripts(self):
343 """ return the ifupdown scripts replaced by the current module """
344 try:
345 return self.overrides_ifupdown_scripts
346 except:
347 return []
348
349 def _get_reserved_vlan_range(self):
350 start = end = 0
351 get_resvvlan = '/var/lib/ifupdown2/hooks/get_reserved_vlan_range.sh'
352 if not os.path.exists(get_resvvlan):
353 return (start, end)
354 try:
355 (s, e) = utils.exec_command(get_resvvlan).strip('\n').split('-')
356 start = int(s)
357 end = int(e)
358 except Exception, e:
359 self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
360 # ignore errors
361 pass
362 return (start, end)
363
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]))
372 return True
373 return False
374
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):
378 return False
379 return True