]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdownaddons/modulebase.py
Fixing errors and typos
[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
a4a53f4b
JF
13import signal
14import shlex
15
16from ifupdown.utils import utils
15ef32ea 17from ifupdown.iface import *
717cee31 18import ifupdown.policymanager as policymanager
fc5e1735 19import ifupdown.ifupdownflags as ifupdownflags
15ef32ea
RP
20
21class 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)
15ef32ea 29
717cee31
RP
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
b3745e05 35 def log_warn(self, str, ifaceobj=None):
15ef32ea
RP
36 """ log a warning if err str is not one of which we should ignore """
37 if not self.ignore_error(str):
38 if self.logger.getEffectiveLevel() == logging.DEBUG:
39 traceback.print_stack()
40 self.logger.warn(str)
b3745e05
RP
41 if ifaceobj:
42 ifaceobj.set_status(ifaceStatus.WARNING)
15ef32ea
RP
43 pass
44
bf3eda91 45 def log_error(self, str, ifaceobj=None, raise_error=True):
15ef32ea
RP
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()
b3745e05
RP
50 if ifaceobj:
51 ifaceobj.set_status(ifaceStatus.ERROR)
bf3eda91
RP
52 if raise_error:
53 raise Exception(str)
15ef32ea
RP
54 else:
55 pass
56
fff589ea 57 def is_process_running(self, procName):
58 try:
59 self.exec_command('/bin/pidof -x %s' % procName)
60 except:
61 return False
62 else:
63 return True
64
15ef32ea
RP
65 def exec_command(self, cmd, cmdenv=None):
66 """ execute command passed as argument.
67
68 Args:
69 cmd (str): command to execute
70
71 Kwargs:
72 cmdenv (dict): environment variable name value pairs
73 """
74 cmd_returncode = 0
75 cmdout = ''
76
77 try:
78 self.logger.info('Executing ' + cmd)
fc5e1735 79 if ifupdownflags.flags.DRYRUN:
15ef32ea 80 return cmdout
a4a53f4b 81 ch = subprocess.Popen(shlex.split(cmd),
15ef32ea
RP
82 stdout=subprocess.PIPE,
83 shell=False, env=cmdenv,
84 stderr=subprocess.STDOUT,
85 close_fds=True)
a4a53f4b 86 utils.enable_subprocess_signal_forwarding(ch, signal.SIGINT)
15ef32ea
RP
87 cmdout = ch.communicate()[0]
88 cmd_returncode = ch.wait()
89 except OSError, e:
90 raise Exception('could not execute ' + cmd +
91 '(' + str(e) + ')')
a4a53f4b
JF
92 finally:
93 utils.disable_subprocess_signal_forwarding(signal.SIGINT)
15ef32ea
RP
94 if cmd_returncode != 0:
95 raise Exception('error executing cmd \'%s\'' %cmd +
96 '(' + cmdout.strip('\n ') + ')')
97 return cmdout
98
99 def exec_command_talk_stdin(self, cmd, stdinbuf):
100 """ execute command passed as argument and write contents of stdinbuf
101 into stdin of the cmd
102
103 Args:
104 cmd (str): command to execute
105 stdinbuf (str): string to write to stdin of the cmd process
106 """
107 cmd_returncode = 0
108 cmdout = ''
109
110 try:
111 self.logger.info('Executing %s (stdin=%s)' %(cmd, stdinbuf))
fc5e1735 112 if ifupdownflags.flags.DRYRUN:
15ef32ea 113 return cmdout
a4a53f4b 114 ch = subprocess.Popen(shlex.split(cmd),
15ef32ea
RP
115 stdout=subprocess.PIPE,
116 stdin=subprocess.PIPE,
117 shell=False, env=cmdenv,
118 stderr=subprocess.STDOUT,
119 close_fds=True)
a4a53f4b 120 utils.enable_subprocess_signal_forwarding(ch, signal.SIGINT)
15ef32ea
RP
121 cmdout = ch.communicate(input=stdinbuf)[0]
122 cmd_returncode = ch.wait()
123 except OSError, e:
124 raise Exception('could not execute ' + cmd +
125 '(' + str(e) + ')')
a4a53f4b
JF
126 finally:
127 utils.disable_subprocess_signal_forwarding(signal.SIGINT)
15ef32ea
RP
128 if cmd_returncode != 0:
129 raise Exception('error executing cmd \'%s (%s)\''
130 %(cmd, stdinbuf) + '(' + cmdout.strip('\n ') + ')')
131 return cmdout
132
133 def get_ifaces_from_proc(self):
134 ifacenames = []
135 with open('/proc/net/dev') as f:
136 try:
137 lines = f.readlines()
0c8332bc 138 for line in lines[2:]:
15ef32ea
RP
139 ifacenames.append(line.split()[0].strip(': '))
140 except:
141 raise
142 return ifacenames
143
0c8332bc 144 def parse_regex(self, ifacename, expr, ifacenames=None):
15ef32ea
RP
145 try:
146 proc_ifacenames = self.get_ifaces_from_proc()
147 except:
0c8332bc
RP
148 self.logger.warn('%s: error reading ifaces from proc' %ifacename)
149
15ef32ea 150 for proc_ifacename in proc_ifacenames:
0c8332bc
RP
151 try:
152 if re.search(expr + '$', proc_ifacename):
153 yield proc_ifacename
154 except Exception, e:
155 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
156 %(ifacename, expr, proc_ifacename, str(e)))
15ef32ea
RP
157 if not ifacenames:
158 return
159 for ifacename in ifacenames:
0c8332bc
RP
160 try:
161 if re.search(expr + '$', ifacename):
162 yield ifacename
163 except Exception, e:
164 raise Exception('%s: error searching regex \'%s\' in %s (%s)'
165 %(ifacename, expr, ifacename, str(e)))
15ef32ea 166
0c8332bc 167 def parse_glob(self, ifacename, expr):
15ef32ea 168 errmsg = ('error parsing glob expression \'%s\'' %expr +
139662ee
ST
169 ' (supported glob syntax: swp1-10.300 or swp[1-10].300' +
170 ' or swp[1-10]sub[0-4].300')
171
172 # explanations are shown below in each if clause
173 regexs = [re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\]([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)"),
174 re.compile(r"([A-Za-z0-9\-]+[A-Za-z])(\d+)\-(\d+)(.*)"),
175 re.compile(r"([A-Za-z0-9\-]+)\[(\d+)\-(\d+)\](.*)")]
176
177 if regexs[0].match(expr):
178 # the first regex checks for exactly two levels of ranges defined only with square brackets
179 # (e.g. swpxyz[10-23]subqwe[0-4].100) to handle naming with two levels of port names.
180 m = regexs[0].match(expr)
181 mlist = m.groups()
182 if len(mlist) < 7:
183 # we have problems and should not continue
0c8332bc 184 raise Exception('%s: error: unhandled glob expression %s\n%s' % (ifacename, expr,errmsg))
139662ee
ST
185
186 prefix = mlist[0]
187 suffix = mlist[6]
188 start_index = int(mlist[1])
189 end_index = int(mlist[2])
190 sub_string = mlist[3]
191 start_sub = int(mlist[4])
192 end_sub = int(mlist[5])
193 for i in range(start_index, end_index + 1):
194 for j in range(start_sub, end_sub + 1):
195 yield prefix + '%d%s%d' % (i,sub_string,j) + suffix
196
197 elif regexs[1].match(expr) or regexs[2].match(expr):
198 # the second regex for 1 level with a range (e.g. swp10-14.100
199 # the third regex checks for 1 level with [] (e.g. swp[10-14].100)
200 start_index = 0
201 end_index = 0
202 if regexs[1].match(expr):
203 m = regexs[1].match(expr)
204 else:
205 m = regexs[2].match(expr)
206 mlist = m.groups()
207 if len(mlist) != 4:
0c8332bc 208 raise Exception('%s: ' %ifacename + errmsg + '(unexpected len)')
139662ee
ST
209 prefix = mlist[0]
210 suffix = mlist[3]
211 start_index = int(mlist[1])
212 end_index = int(mlist[2])
15ef32ea
RP
213 for i in range(start_index, end_index + 1):
214 yield prefix + '%d' %i + suffix
215
139662ee
ST
216 else:
217 # Could not match anything.
0c8332bc 218 self.logger.warn('%s: %s' %(ifacename, errmsg))
139662ee
ST
219 yield expr
220
0c8332bc 221 def parse_port_list(self, ifacename, port_expr, ifacenames=None):
15ef32ea
RP
222 """ parse port list containing glob and regex
223
224 Args:
225 port_expr (str): expression
226 ifacenames (list): list of interface names. This needs to be specified if the expression has a regular expression
227 """
228 regex = 0
229 glob = 0
230 portlist = []
231
232 if not port_expr:
233 return None
0c8332bc 234 exprs = re.split(r'[\s\t]\s*', port_expr)
77d9d664 235 self.logger.debug('%s: evaluating port expr \'%s\''
0c8332bc
RP
236 %(ifacename, str(exprs)))
237 for expr in exprs:
84ca006f
RP
238 if expr == 'noregex':
239 regex = 0
240 elif expr == 'noglob':
241 glob = 0
242 elif expr == 'regex':
15ef32ea
RP
243 regex = 1
244 elif expr == 'glob':
245 glob = 1
246 elif regex:
0c8332bc 247 for port in self.parse_regex(ifacename, expr, ifacenames):
15ef32ea
RP
248 if port not in portlist:
249 portlist.append(port)
250 regex = 0
251 elif glob:
9a8ad21a 252 for port in self.parse_glob(ifacename, expr):
15ef32ea
RP
253 portlist.append(port)
254 glob = 0
255 else:
256 portlist.append(expr)
257 if not portlist:
258 return None
259 return portlist
260
261 def ignore_error(self, errmsg):
fc5e1735 262 if (ifupdownflags.flags.FORCE or re.search(r'exists', errmsg,
15ef32ea
RP
263 re.IGNORECASE | re.MULTILINE)):
264 return True
265 return False
266
267 def write_file(self, filename, strexpr):
268 """ writes string to a file """
269 try:
270 self.logger.info('writing \'%s\'' %strexpr +
271 ' to file %s' %filename)
fc5e1735 272 if ifupdownflags.flags.DRYRUN:
15ef32ea
RP
273 return 0
274 with open(filename, 'w') as f:
275 f.write(strexpr)
276 except IOError, e:
277 self.logger.warn('error writing to file %s'
278 %filename + '(' + str(e) + ')')
279 return -1
280 return 0
281
282 def read_file(self, filename):
283 """ read file and return lines from the file """
284 try:
285 self.logger.info('reading \'%s\'' %filename)
286 with open(filename, 'r') as f:
287 return f.readlines()
288 except:
289 return None
290 return None
291
292 def read_file_oneline(self, filename):
293 """ reads and returns first line from the file """
294 try:
295 self.logger.info('reading \'%s\'' %filename)
296 with open(filename, 'r') as f:
297 return f.readline().strip('\n')
298 except:
299 return None
300 return None
301
302 def sysctl_set(self, variable, value):
303 """ set sysctl variable to value passed as argument """
304 self.exec_command('sysctl %s=' %variable + '%s' %value)
305
306 def sysctl_get(self, variable):
307 """ get value of sysctl variable """
308 return self.exec_command('sysctl %s' %variable).split('=')[1].strip()
309
310 def set_iface_attr(self, ifaceobj, attr_name, attr_valsetfunc,
311 prehook=None, prehookargs=None):
312 ifacename = ifaceobj.name
313 attrvalue = ifaceobj.get_attr_value_first(attr_name)
314 if attrvalue:
315 if prehook:
316 if prehookargs:
317 prehook(prehookargs)
318 else:
319 prehook(ifacename)
320 attr_valsetfunc(ifacename, attrvalue)
321
322 def query_n_update_ifaceobjcurr_attr(self, ifaceobj, ifaceobjcurr,
323 attr_name, attr_valgetfunc,
324 attr_valgetextraarg=None):
325 attrvalue = ifaceobj.get_attr_value_first(attr_name)
326 if not attrvalue:
327 return
328 if attr_valgetextraarg:
329 runningattrvalue = attr_valgetfunc(ifaceobj.name,
330 attr_valgetextraarg)
331 else:
332 runningattrvalue = attr_valgetfunc(ifaceobj.name)
333 if (not runningattrvalue or
334 (runningattrvalue != attrvalue)):
335 ifaceobjcurr.update_config_with_status(attr_name,
336 runningattrvalue, 1)
337 else:
338 ifaceobjcurr.update_config_with_status(attr_name,
339 runningattrvalue, 0)
340
341 def dict_key_subset(self, a, b):
342 """ returns a list of differing keys """
343 return [x for x in a if x in b]
344
345 def get_mod_attrs(self):
1553a881
RP
346 """ returns list of all module attrs defined in the module _modinfo
347 dict
348 """
15ef32ea 349 try:
1553a881
RP
350 retattrs = []
351 attrsdict = self._modinfo.get('attrs')
352 for attrname, attrvals in attrsdict.iteritems():
353 if not attrvals or attrvals.get('deprecated'):
354 continue
355 retattrs.append(attrname)
356 return retattrs
15ef32ea
RP
357 except:
358 return None
359
360 def get_mod_attr(self, attrname):
361 """ returns module attr info """
362 try:
363 return self._modinfo.get('attrs', {}).get(attrname)
364 except:
365 return None
366
367 def get_mod_subattr(self, attrname, subattrname):
368 """ returns module attrs defined in the module _modinfo dict"""
369 try:
370 return reduce(lambda d, k: d[k], ['attrs', attrname, subattrname],
371 self._modinfo)
372 except:
373 return None
374
375 def get_modinfo(self):
376 """ return module info """
377 try:
378 return self._modinfo
379 except:
380 return None
381
2da58137
RP
382 def _get_reserved_vlan_range(self):
383 start = end = 0
384 get_resvvlan = '/usr/share/python-ifupdown2/get_reserved_vlan_range.sh'
19f8bb0b
RP
385 if not os.path.exists(get_resvvlan):
386 return (start, end)
2da58137 387 try:
ad25e7bb
RP
388 (s, e) = self.exec_command(get_resvvlan).strip('\n').split('-')
389 start = int(s)
390 end = int(e)
19f8bb0b
RP
391 except Exception, e:
392 self.logger.debug('%s failed (%s)' %(get_resvvlan, str(e)))
2da58137
RP
393 # ignore errors
394 pass
395 return (start, end)
cd3059b8 396
2708f915 397 def _handle_reserved_vlan(self, vlanid, logprefix=''):
cd3059b8
RP
398 """ Helper function to check and warn if the vlanid falls in the
399 reserved vlan range """
400 if vlanid in range(self._resv_vlan_range[0],
401 self._resv_vlan_range[1]):
2708f915
RP
402 self.logger.error('%s: reserved vlan %d being used'
403 %(logprefix, vlanid) + ' (reserved vlan range %d-%d)'
404 %(self._resv_vlan_range[0], self._resv_vlan_range[1]))
cd3059b8
RP
405 return True
406 return False
5828d8c5
RP
407
408 def _valid_ethaddr(self, ethaddr):
409 """ Check if address is 00:00:00:00:00:00 """
410 if not ethaddr or re.match('00:00:00:00:00:00', ethaddr):
411 return False
412 return True