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