]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdownaddons/modulebase.py
addons: address: avoids quagga configured address deletion when no address-purge
[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
11import subprocess
12import traceback
13from ifupdown.iface import *
14#from ifupdownaddons.iproute2 import *
15#from ifupdownaddons.dhclient import *
16#from ifupdownaddons.bridgeutils import *
17#from ifupdownaddons.mstpctlutil import *
6cb589df 18#from ifupdownaddons.bondutil import *
15ef32ea
RP
19
20class 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
fff589ea 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
15ef32ea
RP
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()
0c8332bc 129 for line in lines[2:]:
15ef32ea
RP
130 ifacenames.append(line.split()[0].strip(': '))
131 except:
132 raise
133 return ifacenames
134
0c8332bc 135 def parse_regex(self, ifacename, expr, ifacenames=None):
15ef32ea
RP
136 try:
137 proc_ifacenames = self.get_ifaces_from_proc()
138 except:
0c8332bc
RP
139 self.logger.warn('%s: error reading ifaces from proc' %ifacename)
140
15ef32ea 141 for proc_ifacename in proc_ifacenames:
0c8332bc
RP
142 try:
143 if re.search(expr + '$', proc_ifacename):
144 yield proc_ifacename
145 except Exception, e:
146 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
147 %(ifacename, expr, proc_ifacename, str(e)))
15ef32ea
RP
148 if not ifacenames:
149 return
150 for ifacename in ifacenames:
0c8332bc
RP
151 try:
152 if re.search(expr + '$', ifacename):
153 yield ifacename
154 except Exception, e:
155 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
156 %(ifacename, expr, ifacename, str(e)))
15ef32ea 157
0c8332bc 158 def parse_glob(self, ifacename, expr):
15ef32ea 159 errmsg = ('error parsing glob expression \'%s\'' %expr +
139662ee
ST
160 ' (supported glob syntax: swp1-10.300 or swp[1-10].300' +
161 ' or swp[1-10]sub[0-4].300')
162
163 # explanations are shown below in each if clause
164 regexs = [re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\]([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)"),
165 re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
166 re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
167
168 if regexs[0].match(expr):
169 # the first regex checks for exactly two levels of ranges defined only with square brackets
170 # (e.g. swpxyz[10-23]subqwe[0-4].100) to handle naming with two levels of port names.
171 m = regexs[0].match(expr)
172 mlist = m.groups()
173 if len(mlist) < 7:
174 # we have problems and should not continue
0c8332bc 175 raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename, expr,errmsg))
139662ee
ST
176
177 prefix = mlist[0]
178 suffix = mlist[6]
179 start_index = int(mlist[1])
180 end_index = int(mlist[2])
181 sub_string = mlist[3]
182 start_sub = int(mlist[4])
183 end_sub = int(mlist[5])
184 for i in range(start_index, end_index + 1):
185 for j in range(start_sub, end_sub + 1):
186 yield prefix + '%d%s%d' % (i,sub_string,j) + suffix
187
188 elif regexs[1].match(expr) or regexs[2].match(expr):
189 # the second regex for 1 level with a range (e.g. swp10-14.100
190 # the third regex checks for 1 level with [] (e.g. swp[10-14].100)
191 start_index = 0
192 end_index = 0
193 if regexs[1].match(expr):
194 m = regexs[1].match(expr)
195 else:
196 m = regexs[2].match(expr)
197 mlist = m.groups()
198 if len(mlist) != 4:
0c8332bc 199 raise Exception('%s: ' %ifacename + errmsg + '(unexpected len)')
139662ee
ST
200 prefix = mlist[0]
201 suffix = mlist[3]
202 start_index = int(mlist[1])
203 end_index = int(mlist[2])
15ef32ea
RP
204 for i in range(start_index, end_index + 1):
205 yield prefix + '%d' %i + suffix
206
139662ee
ST
207 else:
208 # Could not match anything.
0c8332bc 209 self.logger.warn('%s: %s' %(ifacename, errmsg))
139662ee
ST
210 yield expr
211
0c8332bc 212 def parse_port_list(self, ifacename, port_expr, ifacenames=None):
15ef32ea
RP
213 """ parse port list containing glob and regex
214
215 Args:
216 port_expr (str): expression
217 ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
218 """
219 regex = 0
220 glob = 0
221 portlist = []
222
223 if not port_expr:
224 return None
0c8332bc
RP
225 exprs = re.split(r'[\s\t]\s*', port_expr)
226 self.logger.info('%s: evaluating port expr \'%s\''
227 %(ifacename, str(exprs)))
228 for expr in exprs:
84ca006f
RP
229 if expr == 'noregex':
230 regex = 0
231 elif expr == 'noglob':
232 glob = 0
233 elif expr == 'regex':
15ef32ea
RP
234 regex = 1
235 elif expr == 'glob':
236 glob = 1
237 elif regex:
0c8332bc 238 for port in self.parse_regex(ifacename, expr, ifacenames):
15ef32ea
RP
239 if port not in portlist:
240 portlist.append(port)
241 regex = 0
242 elif glob:
9a8ad21a 243 for port in self.parse_glob(ifacename, expr):
15ef32ea
RP
244 portlist.append(port)
245 glob = 0
246 else:
247 portlist.append(expr)
248 if not portlist:
249 return None
250 return portlist
251
252 def ignore_error(self, errmsg):
253 if (self.FORCE or re.search(r'exists', errmsg,
254 re.IGNORECASE | re.MULTILINE)):
255 return True
256 return False
257
258 def write_file(self, filename, strexpr):
259 """ writes string to a file """
260 try:
261 self.logger.info('writing \'%s\'' %strexpr +
262 ' to file %s' %filename)
263 if self.DRYRUN:
264 return 0
265 with open(filename, 'w') as f:
266 f.write(strexpr)
267 except IOError, e:
268 self.logger.warn('error writing to file %s'
269 %filename + '(' + str(e) + ')')
270 return -1
271 return 0
272
273 def read_file(self, filename):
274 """ read file and return lines from the file """
275 try:
276 self.logger.info('reading \'%s\'' %filename)
277 with open(filename, 'r') as f:
278 return f.readlines()
279 except:
280 return None
281 return None
282
283 def read_file_oneline(self, filename):
284 """ reads and returns first line from the file """
285 try:
286 self.logger.info('reading \'%s\'' %filename)
287 with open(filename, 'r') as f:
288 return f.readline().strip('\n')
289 except:
290 return None
291 return None
292
293 def sysctl_set(self, variable, value):
294 """ set sysctl variable to value passed as argument """
295 self.exec_command('sysctl %s=' %variable + '%s' %value)
296
297 def sysctl_get(self, variable):
298 """ get value of sysctl variable """
299 return self.exec_command('sysctl %s' %variable).split('=')[1].strip()
300
301 def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc,
302 prehook=None, prehookargs=None):
303 ifacename = ifaceobj.name
304 attrvalue = ifaceobj.get_attr_value_first(attr_name)
305 if attrvalue:
306 if prehook:
307 if prehookargs:
308 prehook(prehookargs)
309 else:
310 prehook(ifacename)
311 attr_valsetfunc(ifacename, attrvalue)
312
313 def query_n_update_ifaceobjcurr_attr(self, ifaceobj, ifaceobjcurr,
314 attr_name, attr_valgetfunc,
315 attr_valgetextraarg=None):
316 attrvalue = ifaceobj.get_attr_value_first(attr_name)
317 if not attrvalue:
318 return
319 if attr_valgetextraarg:
320 runningattrvalue = attr_valgetfunc(ifaceobj.name,
321 attr_valgetextraarg)
322 else:
323 runningattrvalue = attr_valgetfunc(ifaceobj.name)
324 if (not runningattrvalue or
325 (runningattrvalue != attrvalue)):
326 ifaceobjcurr.update_config_with_status(attr_name,
327 runningattrvalue, 1)
328 else:
329 ifaceobjcurr.update_config_with_status(attr_name,
330 runningattrvalue, 0)
331
332 def dict_key_subset(self, a, b):
333 """ returns a list of differing keys """
334 return [x for x in a if x in b]
335
336 def get_mod_attrs(self):
337 """ returns list of all module attrs defined in the module _modinfo dict"""
338 try:
339 return self._modinfo.get('attrs').keys()
340 except:
341 return None
342
343 def get_mod_attr(self, attrname):
344 """ returns module attr info """
345 try:
346 return self._modinfo.get('attrs', {}).get(attrname)
347 except:
348 return None
349
350 def get_mod_subattr(self, attrname, subattrname):
351 """ returns module attrs defined in the module _modinfo dict"""
352 try:
353 return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
354 self._modinfo)
355 except:
356 return None
357
358 def get_modinfo(self):
359 """ return module info """
360 try:
361 return self._modinfo
362 except:
363 return None
364
365 def get_flags(self):
366 return dict(force=self.FORCE, dryrun=self.DRYRUN, nowait=self.NOWAIT,
367 perfmode=self.PERFMODE, cache=self.CACHE,
368 cacheflags=self.CACHE_FLAGS)
2da58137
RP
369
370 def _get_reserved_vlan_range(self):
371 start = end = 0
372 get_resvvlan = '/usr/share/python-ifupdown2/get_reserved_vlan_range.sh'
19f8bb0b
RP
373 if not os.path.exists(get_resvvlan):
374 return (start, end)
2da58137 375 try:
ad25e7bb
RP
376 (s, e) = self.exec_command(get_resvvlan).strip('\n').split('-')
377 start = int(s)
378 end = int(e)
19f8bb0b
RP
379 except Exception, e:
380 self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
2da58137
RP
381 # ignore errors
382 pass
383 return (start, end)
cd3059b8 384
2708f915 385 def _handle_reserved_vlan(self, vlanid, logprefix=''):
cd3059b8
RP
386 """ Helper function to check and warn if the vlanid falls in the
387 reserved vlan range """
388 if vlanid in range(self._resv_vlan_range[0],
389 self._resv_vlan_range[1]):
2708f915
RP
390 self.logger.error('%s: reserved vlan %d being used'
391 %(logprefix, vlanid) + ' (reserved vlan range %d-%d)'
392 %(self._resv_vlan_range[0], self._resv_vlan_range[1]))
cd3059b8
RP
393 return True
394 return False
5828d8c5
RP
395
396 def _valid_ethaddr(self, ethaddr):
397 """ Check if address is 00:00:00:00:00:00 """
398 if not ethaddr or re.match('00:00:00:00:00:00', ethaddr):
399 return False
400 return True