]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/ifupdownaddons/modulebase.py
Merge 'vlan filtering bridge + vxlan + mlag + vrr' support from internal
[mirror_ifupdown2.git] / ifupdown2 / 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 subprocess
12 import traceback
13 from ifupdown.iface import *
14 #from ifupdownaddons.iproute2 import *
15 #from ifupdownaddons.dhclient import *
16 #from ifupdownaddons.bridgeutils import *
17 #from ifupdownaddons.mstpctlutil import *
18 #from ifupdownaddons.ifenslaveutil import *
19
20 class moduleBase(object):
21 """ Base class for ifupdown addon modules
22
23 Provides common infrastructure methods for all addon modules """
24
25 def __init__(self, *args, **kargs):
26 modulename = self.__class__.__name__
27 self.logger = logging.getLogger('ifupdown.' + modulename)
28 self.FORCE = kargs.get('force', False)
29 """force interface configuration"""
30 self.DRYRUN = kargs.get('dryrun', False)
31 """only predend you are applying configuration, dont really do it"""
32 self.NOWAIT = kargs.get('nowait', False)
33 self.PERFMODE = kargs.get('perfmode', False)
34 self.CACHE = kargs.get('cache', False)
35 self.CACHE_FLAGS = kargs.get('cacheflags', 0x0)
36
37 def log_warn(self, str):
38 """ log a warning if err str is not one of which we should ignore """
39 if not self.ignore_error(str):
40 if self.logger.getEffectiveLevel() == logging.DEBUG:
41 traceback.print_stack()
42 self.logger.warn(str)
43 pass
44
45 def log_error(self, str):
46 """ log an err if err str is not one of which we should ignore and raise an exception """
47 if not self.ignore_error(str):
48 if self.logger.getEffectiveLevel() == logging.DEBUG:
49 traceback.print_stack()
50 raise Exception(str)
51 else:
52 pass
53
54 def is_process_running(self, procName):
55 try:
56 self.exec_command('/bin/pidof -x %s' % procName)
57 except:
58 return False
59 else:
60 return True
61
62 def exec_command(self, cmd, cmdenv=None):
63 """ execute command passed as argument.
64
65 Args:
66 cmd (str): command to execute
67
68 Kwargs:
69 cmdenv (dict): environment variable name value pairs
70 """
71 cmd_returncode = 0
72 cmdout = ''
73
74 try:
75 self.logger.info('Executing ' + cmd)
76 if self.DRYRUN:
77 return cmdout
78 ch = subprocess.Popen(cmd.split(),
79 stdout=subprocess.PIPE,
80 shell=False, env=cmdenv,
81 stderr=subprocess.STDOUT,
82 close_fds=True)
83 cmdout = ch.communicate()[0]
84 cmd_returncode = ch.wait()
85 except OSError, e:
86 raise Exception('could not execute ' + cmd +
87 '(' + str(e) + ')')
88 if cmd_returncode != 0:
89 raise Exception('error executing cmd \'%s\'' %cmd +
90 '(' + cmdout.strip('\n ') + ')')
91 return cmdout
92
93 def exec_command_talk_stdin(self, cmd, stdinbuf):
94 """ execute command passed as argument and write contents of stdinbuf
95 into stdin of the cmd
96
97 Args:
98 cmd (str): command to execute
99 stdinbuf (str): string to write to stdin of the cmd process
100 """
101 cmd_returncode = 0
102 cmdout = ''
103
104 try:
105 self.logger.info('Executing %s (stdin=%s)' %(cmd, stdinbuf))
106 if self.DRYRUN:
107 return cmdout
108 ch = subprocess.Popen(cmd.split(),
109 stdout=subprocess.PIPE,
110 stdin=subprocess.PIPE,
111 shell=False, env=cmdenv,
112 stderr=subprocess.STDOUT,
113 close_fds=True)
114 cmdout = ch.communicate(input=stdinbuf)[0]
115 cmd_returncode = ch.wait()
116 except OSError, e:
117 raise Exception('could not execute ' + cmd +
118 '(' + str(e) + ')')
119 if cmd_returncode != 0:
120 raise Exception('error executing cmd \'%s (%s)\''
121 %(cmd, stdinbuf) + '(' + cmdout.strip('\n ') + ')')
122 return cmdout
123
124 def get_ifaces_from_proc(self):
125 ifacenames = []
126 with open('/proc/net/dev') as f:
127 try:
128 lines = f.readlines()
129 for line in lines:
130 ifacenames.append(line.split()[0].strip(': '))
131 except:
132 raise
133 return ifacenames
134
135 def parse_regex(self, expr, ifacenames=None):
136 try:
137 proc_ifacenames = self.get_ifaces_from_proc()
138 except:
139 self.logger.warn('error reading ifaces from proc')
140 for proc_ifacename in proc_ifacenames:
141 if re.search(expr + '$', proc_ifacename):
142 yield proc_ifacename
143 if not ifacenames:
144 return
145 for ifacename in ifacenames:
146 if re.search(expr + '$', ifacename):
147 yield ifacename
148
149 def parse_glob(self, expr):
150 errmsg = ('error parsing glob expression \'%s\'' %expr +
151 ' (supported glob syntax: swp1-10 or swp[1-10])')
152 start_index = 0
153 end_index = 0
154 try:
155 regexs = [re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
156 re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
157 for r in regexs:
158 m = r.match(expr)
159 if not m:
160 continue
161 mlist = m.groups()
162 if len(mlist) != 4:
163 raise Exception(errmsg + '(unexpected len)')
164 prefix = mlist[0]
165 suffix = mlist[3]
166 start_index = int(mlist[1])
167 end_index = int(mlist[2])
168 except:
169 self.logger.warn(errmsg)
170 pass
171 if not start_index and not end_index:
172 self.logger.warn(errmsg)
173 yield expr
174 else:
175 for i in range(start_index, end_index + 1):
176 yield prefix + '%d' %i + suffix
177
178 def parse_port_list(self, port_expr, ifacenames=None):
179 """ parse port list containing glob and regex
180
181 Args:
182 port_expr (str): expression
183 ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
184 """
185 regex = 0
186 glob = 0
187 portlist = []
188
189 if not port_expr:
190 return None
191 for expr in re.split(r'[\s\t]\s*', port_expr):
192 if expr == 'noregex':
193 regex = 0
194 elif expr == 'noglob':
195 glob = 0
196 elif expr == 'regex':
197 regex = 1
198 elif expr == 'glob':
199 glob = 1
200 elif regex:
201 for port in self.parse_regex(expr, ifacenames):
202 if port not in portlist:
203 portlist.append(port)
204 regex = 0
205 elif glob:
206 for port in self.parse_glob(expr):
207 portlist.append(port)
208 glob = 0
209 else:
210 portlist.append(expr)
211 if not portlist:
212 return None
213 return portlist
214
215 def ignore_error(self, errmsg):
216 if (self.FORCE or re.search(r'exists', errmsg,
217 re.IGNORECASE | re.MULTILINE)):
218 return True
219 return False
220
221 def write_file(self, filename, strexpr):
222 """ writes string to a file """
223 try:
224 self.logger.info('writing \'%s\'' %strexpr +
225 ' to file %s' %filename)
226 if self.DRYRUN:
227 return 0
228 with open(filename, 'w') as f:
229 f.write(strexpr)
230 except IOError, e:
231 self.logger.warn('error writing to file %s'
232 %filename + '(' + str(e) + ')')
233 return -1
234 return 0
235
236 def read_file(self, filename):
237 """ read file and return lines from the file """
238 try:
239 self.logger.info('reading \'%s\'' %filename)
240 with open(filename, 'r') as f:
241 return f.readlines()
242 except:
243 return None
244 return None
245
246 def read_file_oneline(self, filename):
247 """ reads and returns first line from the file """
248 try:
249 self.logger.info('reading \'%s\'' %filename)
250 with open(filename, 'r') as f:
251 return f.readline().strip('\n')
252 except:
253 return None
254 return None
255
256 def sysctl_set(self, variable, value):
257 """ set sysctl variable to value passed as argument """
258 self.exec_command('sysctl %s=' %variable + '%s' %value)
259
260 def sysctl_get(self, variable):
261 """ get value of sysctl variable """
262 return self.exec_command('sysctl %s' %variable).split('=')[1].strip()
263
264 def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc,
265 prehook=None, prehookargs=None):
266 ifacename = ifaceobj.name
267 attrvalue = ifaceobj.get_attr_value_first(attr_name)
268 if attrvalue:
269 if prehook:
270 if prehookargs:
271 prehook(prehookargs)
272 else:
273 prehook(ifacename)
274 attr_valsetfunc(ifacename, attrvalue)
275
276 def query_n_update_ifaceobjcurr_attr(self, ifaceobj, ifaceobjcurr,
277 attr_name, attr_valgetfunc,
278 attr_valgetextraarg=None):
279 attrvalue = ifaceobj.get_attr_value_first(attr_name)
280 if not attrvalue:
281 return
282 if attr_valgetextraarg:
283 runningattrvalue = attr_valgetfunc(ifaceobj.name,
284 attr_valgetextraarg)
285 else:
286 runningattrvalue = attr_valgetfunc(ifaceobj.name)
287 if (not runningattrvalue or
288 (runningattrvalue != attrvalue)):
289 ifaceobjcurr.update_config_with_status(attr_name,
290 runningattrvalue, 1)
291 else:
292 ifaceobjcurr.update_config_with_status(attr_name,
293 runningattrvalue, 0)
294
295 def dict_key_subset(self, a, b):
296 """ returns a list of differing keys """
297 return [x for x in a if x in b]
298
299 def get_mod_attrs(self):
300 """ returns list of all module attrs defined in the module _modinfo dict"""
301 try:
302 return self._modinfo.get('attrs').keys()
303 except:
304 return None
305
306 def get_mod_attr(self, attrname):
307 """ returns module attr info """
308 try:
309 return self._modinfo.get('attrs', {}).get(attrname)
310 except:
311 return None
312
313 def get_mod_subattr(self, attrname, subattrname):
314 """ returns module attrs defined in the module _modinfo dict"""
315 try:
316 return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
317 self._modinfo)
318 except:
319 return None
320
321 def get_modinfo(self):
322 """ return module info """
323 try:
324 return self._modinfo
325 except:
326 return None
327
328 def get_flags(self):
329 return dict(force=self.FORCE, dryrun=self.DRYRUN, nowait=self.NOWAIT,
330 perfmode=self.PERFMODE, cache=self.CACHE,
331 cacheflags=self.CACHE_FLAGS)
332
333 def _get_reserved_vlan_range(self):
334 start = end = 0
335 get_resvvlan = '/usr/share/python-ifupdown2/get_reserved_vlan_range.sh'
336 if not os.path.exists(get_resvvlan):
337 return (start, end)
338 try:
339 (s, e) = self.exec_command(get_resvvlan).strip('\n').split('-')
340 start = int(s)
341 end = int(e)
342 except Exception, e:
343 self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
344 # ignore errors
345 pass
346 return (start, end)
347
348 def _handle_reserved_vlan(self, vlanid, logprefix=''):
349 """ Helper function to check and warn if the vlanid falls in the
350 reserved vlan range """
351 if vlanid in range(self._resv_vlan_range[0],
352 self._resv_vlan_range[1]):
353 self.logger.error('%s: reserved vlan %d being used'
354 %(logprefix, vlanid) + ' (reserved vlan range %d-%d)'
355 %(self._resv_vlan_range[0], self._resv_vlan_range[1]))
356 return True
357 return False
358
359 def _valid_ethaddr(self, ethaddr):
360 """ Check if address is 00:00:00:00:00:00 """
361 if not ethaddr or re.match('00:00:00:00:00:00', ethaddr):
362 return False
363 return True