]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown/utils.py
Merge pull request #80 from BarbarossaTM/tunnel-fixes-master
[mirror_ifupdown2.git] / ifupdown / utils.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6 # utils --
7 # helper class
8 #
9
10 import os
11 import re
12 import shlex
13 import fcntl
14 import signal
15 import logging
16 import subprocess
17 import ifupdownflags
18
19 from functools import partial
20 from ipaddr import IPNetwork, IPAddress
21
22 from ifupdown.iface import *
23
24 def signal_handler_f(ps, sig, frame):
25 if ps:
26 ps.send_signal(sig)
27 if sig == signal.SIGINT:
28 raise KeyboardInterrupt
29
30 class utils():
31 logger = logging.getLogger('ifupdown')
32 DEVNULL = open(os.devnull, 'w')
33
34 _string_values = {
35 "on": True,
36 "yes": True,
37 "1": True,
38 "off": False,
39 "no": False,
40 "0": False,
41 }
42
43 _binary_bool = {
44 True: "1",
45 False: "0",
46 }
47
48 _yesno_bool = {
49 True: 'yes',
50 False: 'no'
51 }
52
53 _onoff_bool = {
54 'yes': 'on',
55 'no': 'off'
56 }
57
58 @staticmethod
59 def get_onoff_bool(value):
60 if value in utils._onoff_bool:
61 return utils._onoff_bool[value]
62 return value
63
64 @staticmethod
65 def get_boolean_from_string(value):
66 if value in utils._string_values:
67 return utils._string_values[value]
68 return False
69
70 @staticmethod
71 def get_yesno_boolean(bool):
72 return utils._yesno_bool[bool]
73
74 @staticmethod
75 def boolean_support_binary(value):
76 return utils._binary_bool[utils.get_boolean_from_string(value)]
77
78 @staticmethod
79 def is_binary_bool(value):
80 return value == '0' or value == '1'
81
82 @staticmethod
83 def support_yesno_attrs(attrsdict, attrslist, ifaceobj=None):
84 if ifaceobj:
85 for attr in attrslist:
86 value = ifaceobj.get_attr_value_first(attr)
87 if value and not utils.is_binary_bool(value):
88 if attr in attrsdict:
89 bool = utils.get_boolean_from_string(attrsdict[attr])
90 attrsdict[attr] = utils.get_yesno_boolean(bool)
91 else:
92 for attr in attrslist:
93 if attr in attrsdict:
94 attrsdict[attr] = utils.boolean_support_binary(attrsdict[attr])
95
96 @classmethod
97 def importName(cls, modulename, name):
98 """ Import a named object """
99 try:
100 module = __import__(modulename, globals(), locals(), [name])
101 except ImportError:
102 return None
103 return getattr(module, name)
104
105 @classmethod
106 def lockFile(cls, lockfile):
107 try:
108 fp = os.open(lockfile, os.O_CREAT | os.O_TRUNC | os.O_WRONLY)
109 fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
110 fcntl.fcntl(fp, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
111 except IOError:
112 return False
113 return True
114
115 @classmethod
116 def parse_iface_range(cls, name):
117 # eg: swp1.[2-100]
118 # return (prefix, range-start, range-end)
119 # eg return ("swp1.", 1, 20, ".100")
120 range_match = re.match("^([\w]+)\[([\d]+)-([\d]+)\]([\.\w]+)", name)
121 if range_match:
122 range_groups = range_match.groups()
123 if range_groups[1] and range_groups[2]:
124 return (range_groups[0], int(range_groups[1], 10),
125 int(range_groups[2], 10), range_groups[3])
126 else:
127 # eg: swp[1-20].100
128 # return (prefix, range-start, range-end, suffix)
129 # eg return ("swp", 1, 20, ".100")
130 range_match = re.match("^([\w\.]+)\[([\d]+)-([\d]+)\]", name)
131 if range_match:
132 range_groups = range_match.groups()
133 if range_groups[1] and range_groups[2]:
134 return (range_groups[0], int(range_groups[1], 10),
135 int(range_groups[2], 10))
136 return None
137
138 @classmethod
139 def expand_iface_range(cls, name):
140 ifacenames = []
141 irange = cls.parse_iface_range(name)
142 if irange:
143 if len(irange) == 3:
144 # eg swp1.[2-4], r = "swp1.", 2, 4)
145 for i in range(irange[1], irange[2]):
146 ifacenames.append('%s%d' %(irange[0], i))
147 elif len(irange) == 4:
148 for i in range(irange[1], irange[2]):
149 # eg swp[2-4].100, r = ("swp", 2, 4, ".100")
150 ifacenames.append('%s%d%s' %(irange[0], i, irange[3]))
151 return ifacenames
152
153 @classmethod
154 def is_ifname_range(cls, name):
155 if '[' in name or ']' in name:
156 return True
157 return False
158
159 @classmethod
160 def check_ifname_size_invalid(cls, name=''):
161 """ IFNAMSIZ in include/linux/if.h is 16 so we check this """
162 IFNAMSIZ = 16
163 if len(name) > IFNAMSIZ - 1:
164 return True
165 else:
166 return False
167
168 @classmethod
169 def enable_subprocess_signal_forwarding(cls, ps, sig):
170 signal.signal(sig, partial(signal_handler_f, ps))
171
172 @classmethod
173 def disable_subprocess_signal_forwarding(cls, sig):
174 signal.signal(sig, signal.SIG_DFL)
175
176 @classmethod
177 def _log_command_exec(cls, cmd, stdin):
178 if stdin:
179 cls.logger.info('executing %s [%s]' % (cmd, stdin))
180 else:
181 cls.logger.info('executing %s' % cmd)
182
183 @classmethod
184 def _format_error(cls, cmd, cmd_returncode, cmd_output, stdin):
185 if type(cmd) is list:
186 cmd = ' '.join(cmd)
187 if stdin:
188 cmd = '%s [%s]' % (cmd, stdin)
189 if cmd_output:
190 return 'cmd \'%s\' failed: returned %d (%s)' % \
191 (cmd, cmd_returncode, cmd_output)
192 else:
193 return 'cmd \'%s\' failed: returned %d' % (cmd, cmd_returncode)
194
195 @classmethod
196 def get_normalized_ip_addr(cls, ifacename, ipaddrs):
197 if not ipaddrs: return None
198 if isinstance(ipaddrs, list):
199 addrs = []
200 for ip in ipaddrs:
201 if not ip:
202 continue
203 try:
204 addrs.append(str(IPNetwork(ip)) if '/' in ip else str(IPAddress(ip)))
205 except Exception as e:
206 cls.logger.warning('%s: %s' % (ifacename, e))
207 return addrs
208 else:
209 try:
210 return str(IPNetwork(ipaddrs)) if '/' in ipaddrs else str(IPAddress(ipaddrs))
211 except Exception as e:
212 cls.logger.warning('%s: %s' % (ifacename, e))
213 return ipaddrs
214
215 @classmethod
216 def is_addr_ip_allowed_on(cls, ifaceobj, syntax_check=False):
217 msg = ('%s: ignoring ip address. Assigning an IP '
218 'address is not allowed on' % ifaceobj.name)
219 if (ifaceobj.role & ifaceRole.SLAVE
220 and not (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE)):
221 up = None
222 if ifaceobj.upperifaces:
223 up = ifaceobj.upperifaces[0]
224 msg = ('%s enslaved interfaces. %s'
225 % (msg, ('%s is enslaved to %s'
226 % (ifaceobj.name, up)) if up else '')).strip()
227 if syntax_check:
228 cls.logger.warning(msg)
229 else:
230 cls.logger.info(msg)
231 return False
232 elif (ifaceobj.link_kind & ifaceLinkKind.BRIDGE
233 and ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_VLAN_AWARE):
234 msg = '%s bridge vlan aware interfaces'
235 if syntax_check:
236 cls.logger.warning(msg)
237 else:
238 cls.logger.info(msg)
239 return False
240 return True
241
242 @classmethod
243 def _execute_subprocess(cls, cmd,
244 env=None,
245 shell=False,
246 close_fds=False,
247 stdout=True,
248 stdin=None,
249 stderr=subprocess.STDOUT):
250 """
251 exec's commands using subprocess Popen
252 Args:
253 cmd, should be shlex.split if not shell
254 returns: output
255
256 Note: close_fds=True is affecting performance (2~3 times slower)
257 """
258 if ifupdownflags.flags.DRYRUN:
259 return ''
260
261 cmd_output = None
262 try:
263 ch = subprocess.Popen(cmd,
264 env=env,
265 shell=shell,
266 close_fds=close_fds,
267 stdin=subprocess.PIPE if stdin else None,
268 stdout=subprocess.PIPE if stdout else cls.DEVNULL,
269 stderr=stderr)
270 utils.enable_subprocess_signal_forwarding(ch, signal.SIGINT)
271 if stdout or stdin:
272 cmd_output = ch.communicate(input=stdin)[0]
273 cmd_returncode = ch.wait()
274 except Exception as e:
275 raise Exception('cmd \'%s\' failed (%s)' % (' '.join(cmd), str(e)))
276 finally:
277 utils.disable_subprocess_signal_forwarding(signal.SIGINT)
278 if cmd_returncode != 0:
279 raise Exception(cls._format_error(cmd,
280 cmd_returncode,
281 cmd_output,
282 stdin))
283 return cmd_output
284
285 @classmethod
286 def exec_user_command(cls, cmd, close_fds=False, stdout=True,
287 stdin=None, stderr=subprocess.STDOUT):
288 cls._log_command_exec(cmd, stdin)
289 return cls._execute_subprocess(cmd,
290 shell=True,
291 close_fds=close_fds,
292 stdout=stdout,
293 stdin=stdin,
294 stderr=stderr)
295
296 @classmethod
297 def exec_command(cls, cmd, env=None, close_fds=False, stdout=True,
298 stdin=None, stderr=subprocess.STDOUT):
299 cls._log_command_exec(cmd, stdin)
300 return cls._execute_subprocess(shlex.split(cmd),
301 env=env,
302 close_fds=close_fds,
303 stdout=stdout,
304 stdin=stdin,
305 stderr=stderr)
306
307 @classmethod
308 def exec_commandl(cls, cmdl, env=None, close_fds=False, stdout=True,
309 stdin=None, stderr=subprocess.STDOUT):
310 cls._log_command_exec(' '.join(cmdl), stdin)
311 return cls._execute_subprocess(cmdl,
312 env=env,
313 close_fds=close_fds,
314 stdout=stdout,
315 stdin=stdin,
316 stderr=stderr)
317
318 fcntl.fcntl(utils.DEVNULL, fcntl.F_SETFD, fcntl.FD_CLOEXEC)