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