root / lib / netutils.py @ b8aa46ed
History | View | Annotate | Download (8.5 kB)
1 | a744b676 | Manuel Franceschini | #
|
---|---|---|---|
2 | a744b676 | Manuel Franceschini | #
|
3 | a744b676 | Manuel Franceschini | |
4 | a744b676 | Manuel Franceschini | # Copyright (C) 2010 Google Inc.
|
5 | a744b676 | Manuel Franceschini | #
|
6 | a744b676 | Manuel Franceschini | # This program is free software; you can redistribute it and/or modify
|
7 | a744b676 | Manuel Franceschini | # it under the terms of the GNU General Public License as published by
|
8 | a744b676 | Manuel Franceschini | # the Free Software Foundation; either version 2 of the License, or
|
9 | a744b676 | Manuel Franceschini | # (at your option) any later version.
|
10 | a744b676 | Manuel Franceschini | #
|
11 | a744b676 | Manuel Franceschini | # This program is distributed in the hope that it will be useful, but
|
12 | a744b676 | Manuel Franceschini | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | a744b676 | Manuel Franceschini | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | a744b676 | Manuel Franceschini | # General Public License for more details.
|
15 | a744b676 | Manuel Franceschini | #
|
16 | a744b676 | Manuel Franceschini | # You should have received a copy of the GNU General Public License
|
17 | a744b676 | Manuel Franceschini | # along with this program; if not, write to the Free Software
|
18 | a744b676 | Manuel Franceschini | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | a744b676 | Manuel Franceschini | # 02110-1301, USA.
|
20 | a744b676 | Manuel Franceschini | |
21 | a744b676 | Manuel Franceschini | |
22 | a744b676 | Manuel Franceschini | """Ganeti network utility module.
|
23 | a744b676 | Manuel Franceschini |
|
24 | a744b676 | Manuel Franceschini | This module holds functions that can be used in both daemons (all) and
|
25 | a744b676 | Manuel Franceschini | the command line scripts.
|
26 | a744b676 | Manuel Franceschini |
|
27 | a744b676 | Manuel Franceschini | """
|
28 | a744b676 | Manuel Franceschini | |
29 | a744b676 | Manuel Franceschini | |
30 | a744b676 | Manuel Franceschini | import errno |
31 | a744b676 | Manuel Franceschini | import re |
32 | a744b676 | Manuel Franceschini | import socket |
33 | a744b676 | Manuel Franceschini | import struct |
34 | a744b676 | Manuel Franceschini | import IN |
35 | a744b676 | Manuel Franceschini | |
36 | a744b676 | Manuel Franceschini | from ganeti import constants |
37 | a744b676 | Manuel Franceschini | from ganeti import errors |
38 | a744b676 | Manuel Franceschini | |
39 | a744b676 | Manuel Franceschini | # Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
|
40 | a744b676 | Manuel Franceschini | # struct ucred { pid_t pid; uid_t uid; gid_t gid; };
|
41 | a744b676 | Manuel Franceschini | #
|
42 | a744b676 | Manuel Franceschini | # The GNU C Library defines gid_t and uid_t to be "unsigned int" and
|
43 | a744b676 | Manuel Franceschini | # pid_t to "int".
|
44 | a744b676 | Manuel Franceschini | #
|
45 | a744b676 | Manuel Franceschini | # IEEE Std 1003.1-2008:
|
46 | a744b676 | Manuel Franceschini | # "nlink_t, uid_t, gid_t, and id_t shall be integer types"
|
47 | a744b676 | Manuel Franceschini | # "blksize_t, pid_t, and ssize_t shall be signed integer types"
|
48 | a744b676 | Manuel Franceschini | _STRUCT_UCRED = "iII"
|
49 | a744b676 | Manuel Franceschini | _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED) |
50 | a744b676 | Manuel Franceschini | |
51 | a744b676 | Manuel Franceschini | |
52 | a744b676 | Manuel Franceschini | def GetSocketCredentials(sock): |
53 | a744b676 | Manuel Franceschini | """Returns the credentials of the foreign process connected to a socket.
|
54 | a744b676 | Manuel Franceschini |
|
55 | a744b676 | Manuel Franceschini | @param sock: Unix socket
|
56 | a744b676 | Manuel Franceschini | @rtype: tuple; (number, number, number)
|
57 | a744b676 | Manuel Franceschini | @return: The PID, UID and GID of the connected foreign process.
|
58 | a744b676 | Manuel Franceschini |
|
59 | a744b676 | Manuel Franceschini | """
|
60 | a744b676 | Manuel Franceschini | peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED, |
61 | a744b676 | Manuel Franceschini | _STRUCT_UCRED_SIZE) |
62 | a744b676 | Manuel Franceschini | return struct.unpack(_STRUCT_UCRED, peercred)
|
63 | a744b676 | Manuel Franceschini | |
64 | a744b676 | Manuel Franceschini | |
65 | a744b676 | Manuel Franceschini | def GetHostInfo(name=None): |
66 | a744b676 | Manuel Franceschini | """Lookup host name and raise an OpPrereqError for failures"""
|
67 | a744b676 | Manuel Franceschini | |
68 | a744b676 | Manuel Franceschini | try:
|
69 | a744b676 | Manuel Franceschini | return HostInfo(name)
|
70 | a744b676 | Manuel Franceschini | except errors.ResolverError, err:
|
71 | a744b676 | Manuel Franceschini | raise errors.OpPrereqError("The given name (%s) does not resolve: %s" % |
72 | a744b676 | Manuel Franceschini | (err[0], err[2]), errors.ECODE_RESOLVER) |
73 | a744b676 | Manuel Franceschini | |
74 | a744b676 | Manuel Franceschini | |
75 | a744b676 | Manuel Franceschini | class HostInfo: |
76 | a744b676 | Manuel Franceschini | """Class implementing resolver and hostname functionality
|
77 | a744b676 | Manuel Franceschini |
|
78 | a744b676 | Manuel Franceschini | """
|
79 | a744b676 | Manuel Franceschini | _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
|
80 | a744b676 | Manuel Franceschini | |
81 | a744b676 | Manuel Franceschini | def __init__(self, name=None): |
82 | a744b676 | Manuel Franceschini | """Initialize the host name object.
|
83 | a744b676 | Manuel Franceschini |
|
84 | a744b676 | Manuel Franceschini | If the name argument is not passed, it will use this system's
|
85 | a744b676 | Manuel Franceschini | name.
|
86 | a744b676 | Manuel Franceschini |
|
87 | a744b676 | Manuel Franceschini | """
|
88 | a744b676 | Manuel Franceschini | if name is None: |
89 | a744b676 | Manuel Franceschini | name = self.SysName()
|
90 | a744b676 | Manuel Franceschini | |
91 | a744b676 | Manuel Franceschini | self.query = name
|
92 | a744b676 | Manuel Franceschini | self.name, self.aliases, self.ipaddrs = self.LookupHostname(name) |
93 | a744b676 | Manuel Franceschini | self.ip = self.ipaddrs[0] |
94 | a744b676 | Manuel Franceschini | |
95 | a744b676 | Manuel Franceschini | def ShortName(self): |
96 | a744b676 | Manuel Franceschini | """Returns the hostname without domain.
|
97 | a744b676 | Manuel Franceschini |
|
98 | a744b676 | Manuel Franceschini | """
|
99 | a744b676 | Manuel Franceschini | return self.name.split('.')[0] |
100 | a744b676 | Manuel Franceschini | |
101 | a744b676 | Manuel Franceschini | @staticmethod
|
102 | a744b676 | Manuel Franceschini | def SysName(): |
103 | a744b676 | Manuel Franceschini | """Return the current system's name.
|
104 | a744b676 | Manuel Franceschini |
|
105 | a744b676 | Manuel Franceschini | This is simply a wrapper over C{socket.gethostname()}.
|
106 | a744b676 | Manuel Franceschini |
|
107 | a744b676 | Manuel Franceschini | """
|
108 | a744b676 | Manuel Franceschini | return socket.gethostname()
|
109 | a744b676 | Manuel Franceschini | |
110 | a744b676 | Manuel Franceschini | @staticmethod
|
111 | a744b676 | Manuel Franceschini | def LookupHostname(hostname): |
112 | a744b676 | Manuel Franceschini | """Look up hostname
|
113 | a744b676 | Manuel Franceschini |
|
114 | a744b676 | Manuel Franceschini | @type hostname: str
|
115 | a744b676 | Manuel Franceschini | @param hostname: hostname to look up
|
116 | a744b676 | Manuel Franceschini |
|
117 | a744b676 | Manuel Franceschini | @rtype: tuple
|
118 | a744b676 | Manuel Franceschini | @return: a tuple (name, aliases, ipaddrs) as returned by
|
119 | a744b676 | Manuel Franceschini | C{socket.gethostbyname_ex}
|
120 | a744b676 | Manuel Franceschini | @raise errors.ResolverError: in case of errors in resolving
|
121 | a744b676 | Manuel Franceschini |
|
122 | a744b676 | Manuel Franceschini | """
|
123 | a744b676 | Manuel Franceschini | try:
|
124 | a744b676 | Manuel Franceschini | result = socket.gethostbyname_ex(hostname) |
125 | a744b676 | Manuel Franceschini | except (socket.gaierror, socket.herror, socket.error), err:
|
126 | a744b676 | Manuel Franceschini | # hostname not found in DNS, or other socket exception in the
|
127 | a744b676 | Manuel Franceschini | # (code, description format)
|
128 | a744b676 | Manuel Franceschini | raise errors.ResolverError(hostname, err.args[0], err.args[1]) |
129 | a744b676 | Manuel Franceschini | |
130 | a744b676 | Manuel Franceschini | return result
|
131 | a744b676 | Manuel Franceschini | |
132 | a744b676 | Manuel Franceschini | @classmethod
|
133 | a744b676 | Manuel Franceschini | def NormalizeName(cls, hostname): |
134 | a744b676 | Manuel Franceschini | """Validate and normalize the given hostname.
|
135 | a744b676 | Manuel Franceschini |
|
136 | a744b676 | Manuel Franceschini | @attention: the validation is a bit more relaxed than the standards
|
137 | a744b676 | Manuel Franceschini | require; most importantly, we allow underscores in names
|
138 | a744b676 | Manuel Franceschini | @raise errors.OpPrereqError: when the name is not valid
|
139 | a744b676 | Manuel Franceschini |
|
140 | a744b676 | Manuel Franceschini | """
|
141 | a744b676 | Manuel Franceschini | hostname = hostname.lower() |
142 | a744b676 | Manuel Franceschini | if (not cls._VALID_NAME_RE.match(hostname) or |
143 | a744b676 | Manuel Franceschini | # double-dots, meaning empty label
|
144 | a744b676 | Manuel Franceschini | ".." in hostname or |
145 | a744b676 | Manuel Franceschini | # empty initial label
|
146 | a744b676 | Manuel Franceschini | hostname.startswith(".")):
|
147 | a744b676 | Manuel Franceschini | raise errors.OpPrereqError("Invalid hostname '%s'" % hostname, |
148 | a744b676 | Manuel Franceschini | errors.ECODE_INVAL) |
149 | a744b676 | Manuel Franceschini | if hostname.endswith("."): |
150 | a744b676 | Manuel Franceschini | hostname = hostname.rstrip(".")
|
151 | a744b676 | Manuel Franceschini | return hostname
|
152 | a744b676 | Manuel Franceschini | |
153 | a744b676 | Manuel Franceschini | |
154 | a744b676 | Manuel Franceschini | def _GenericIsValidIP(family, ip): |
155 | a744b676 | Manuel Franceschini | """Generic internal version of ip validation.
|
156 | a744b676 | Manuel Franceschini |
|
157 | a744b676 | Manuel Franceschini | @type family: int
|
158 | a744b676 | Manuel Franceschini | @param family: socket.AF_INET | socket.AF_INET6
|
159 | a744b676 | Manuel Franceschini | @type ip: str
|
160 | a744b676 | Manuel Franceschini | @param ip: the address to be checked
|
161 | a744b676 | Manuel Franceschini | @rtype: boolean
|
162 | a744b676 | Manuel Franceschini | @return: True if ip is valid, False otherwise
|
163 | a744b676 | Manuel Franceschini |
|
164 | a744b676 | Manuel Franceschini | """
|
165 | a744b676 | Manuel Franceschini | try:
|
166 | a744b676 | Manuel Franceschini | socket.inet_pton(family, ip) |
167 | a744b676 | Manuel Franceschini | return True |
168 | a744b676 | Manuel Franceschini | except socket.error:
|
169 | a744b676 | Manuel Franceschini | return False |
170 | a744b676 | Manuel Franceschini | |
171 | a744b676 | Manuel Franceschini | |
172 | a744b676 | Manuel Franceschini | def IsValidIP4(ip): |
173 | a744b676 | Manuel Franceschini | """Verifies an IPv4 address.
|
174 | a744b676 | Manuel Franceschini |
|
175 | a744b676 | Manuel Franceschini | This function checks if the given address is a valid IPv4 address.
|
176 | a744b676 | Manuel Franceschini |
|
177 | a744b676 | Manuel Franceschini | @type ip: str
|
178 | a744b676 | Manuel Franceschini | @param ip: the address to be checked
|
179 | a744b676 | Manuel Franceschini | @rtype: boolean
|
180 | a744b676 | Manuel Franceschini | @return: True if ip is valid, False otherwise
|
181 | a744b676 | Manuel Franceschini |
|
182 | a744b676 | Manuel Franceschini | """
|
183 | a744b676 | Manuel Franceschini | return _GenericIsValidIP(socket.AF_INET, ip)
|
184 | a744b676 | Manuel Franceschini | |
185 | a744b676 | Manuel Franceschini | |
186 | a744b676 | Manuel Franceschini | def IsValidIP6(ip): |
187 | a744b676 | Manuel Franceschini | """Verifies an IPv6 address.
|
188 | a744b676 | Manuel Franceschini |
|
189 | a744b676 | Manuel Franceschini | This function checks if the given address is a valid IPv6 address.
|
190 | a744b676 | Manuel Franceschini |
|
191 | a744b676 | Manuel Franceschini | @type ip: str
|
192 | a744b676 | Manuel Franceschini | @param ip: the address to be checked
|
193 | a744b676 | Manuel Franceschini | @rtype: boolean
|
194 | a744b676 | Manuel Franceschini | @return: True if ip is valid, False otherwise
|
195 | a744b676 | Manuel Franceschini |
|
196 | a744b676 | Manuel Franceschini | """
|
197 | a744b676 | Manuel Franceschini | return _GenericIsValidIP(socket.AF_INET6, ip)
|
198 | a744b676 | Manuel Franceschini | |
199 | a744b676 | Manuel Franceschini | |
200 | a744b676 | Manuel Franceschini | def IsValidIP(ip): |
201 | a744b676 | Manuel Franceschini | """Verifies an IP address.
|
202 | a744b676 | Manuel Franceschini |
|
203 | a744b676 | Manuel Franceschini | This function checks if the given IP address (both IPv4 and IPv6) is valid.
|
204 | a744b676 | Manuel Franceschini |
|
205 | a744b676 | Manuel Franceschini | @type ip: str
|
206 | a744b676 | Manuel Franceschini | @param ip: the address to be checked
|
207 | a744b676 | Manuel Franceschini | @rtype: boolean
|
208 | a744b676 | Manuel Franceschini | @return: True if ip is valid, False otherwise
|
209 | a744b676 | Manuel Franceschini |
|
210 | a744b676 | Manuel Franceschini | """
|
211 | a744b676 | Manuel Franceschini | return IsValidIP4(ip) or IsValidIP6(ip) |
212 | a744b676 | Manuel Franceschini | |
213 | a744b676 | Manuel Franceschini | |
214 | a744b676 | Manuel Franceschini | def GetAddressFamily(ip): |
215 | a744b676 | Manuel Franceschini | """Get the address family of the given address.
|
216 | a744b676 | Manuel Franceschini |
|
217 | a744b676 | Manuel Franceschini | @type ip: str
|
218 | a744b676 | Manuel Franceschini | @param ip: ip address whose family will be returned
|
219 | a744b676 | Manuel Franceschini | @rtype: int
|
220 | a744b676 | Manuel Franceschini | @return: socket.AF_INET or socket.AF_INET6
|
221 | a744b676 | Manuel Franceschini | @raise errors.GenericError: for invalid addresses
|
222 | a744b676 | Manuel Franceschini |
|
223 | a744b676 | Manuel Franceschini | """
|
224 | a744b676 | Manuel Franceschini | if IsValidIP6(ip):
|
225 | a744b676 | Manuel Franceschini | return socket.AF_INET6
|
226 | a744b676 | Manuel Franceschini | elif IsValidIP4(ip):
|
227 | a744b676 | Manuel Franceschini | return socket.AF_INET
|
228 | a744b676 | Manuel Franceschini | else:
|
229 | a744b676 | Manuel Franceschini | raise errors.GenericError("Address %s not valid" % ip) |
230 | a744b676 | Manuel Franceschini | |
231 | a744b676 | Manuel Franceschini | |
232 | a744b676 | Manuel Franceschini | def TcpPing(target, port, timeout=10, live_port_needed=False, source=None): |
233 | a744b676 | Manuel Franceschini | """Simple ping implementation using TCP connect(2).
|
234 | a744b676 | Manuel Franceschini |
|
235 | a744b676 | Manuel Franceschini | Check if the given IP is reachable by doing attempting a TCP connect
|
236 | a744b676 | Manuel Franceschini | to it.
|
237 | a744b676 | Manuel Franceschini |
|
238 | a744b676 | Manuel Franceschini | @type target: str
|
239 | a744b676 | Manuel Franceschini | @param target: the IP or hostname to ping
|
240 | a744b676 | Manuel Franceschini | @type port: int
|
241 | a744b676 | Manuel Franceschini | @param port: the port to connect to
|
242 | a744b676 | Manuel Franceschini | @type timeout: int
|
243 | a744b676 | Manuel Franceschini | @param timeout: the timeout on the connection attempt
|
244 | a744b676 | Manuel Franceschini | @type live_port_needed: boolean
|
245 | a744b676 | Manuel Franceschini | @param live_port_needed: whether a closed port will cause the
|
246 | a744b676 | Manuel Franceschini | function to return failure, as if there was a timeout
|
247 | a744b676 | Manuel Franceschini | @type source: str or None
|
248 | a744b676 | Manuel Franceschini | @param source: if specified, will cause the connect to be made
|
249 | a744b676 | Manuel Franceschini | from this specific source address; failures to bind other
|
250 | a744b676 | Manuel Franceschini | than C{EADDRNOTAVAIL} will be ignored
|
251 | a744b676 | Manuel Franceschini |
|
252 | a744b676 | Manuel Franceschini | """
|
253 | a744b676 | Manuel Franceschini | try:
|
254 | a744b676 | Manuel Franceschini | family = GetAddressFamily(target) |
255 | a744b676 | Manuel Franceschini | except errors.GenericError:
|
256 | a744b676 | Manuel Franceschini | return False |
257 | a744b676 | Manuel Franceschini | |
258 | a744b676 | Manuel Franceschini | sock = socket.socket(family, socket.SOCK_STREAM) |
259 | a744b676 | Manuel Franceschini | success = False
|
260 | a744b676 | Manuel Franceschini | |
261 | a744b676 | Manuel Franceschini | if source is not None: |
262 | a744b676 | Manuel Franceschini | try:
|
263 | a744b676 | Manuel Franceschini | sock.bind((source, 0))
|
264 | a744b676 | Manuel Franceschini | except socket.error, (errcode, _):
|
265 | a744b676 | Manuel Franceschini | if errcode == errno.EADDRNOTAVAIL:
|
266 | a744b676 | Manuel Franceschini | success = False
|
267 | a744b676 | Manuel Franceschini | |
268 | a744b676 | Manuel Franceschini | sock.settimeout(timeout) |
269 | a744b676 | Manuel Franceschini | |
270 | a744b676 | Manuel Franceschini | try:
|
271 | a744b676 | Manuel Franceschini | sock.connect((target, port)) |
272 | a744b676 | Manuel Franceschini | sock.close() |
273 | a744b676 | Manuel Franceschini | success = True
|
274 | a744b676 | Manuel Franceschini | except socket.timeout:
|
275 | a744b676 | Manuel Franceschini | success = False
|
276 | a744b676 | Manuel Franceschini | except socket.error, (errcode, _):
|
277 | a744b676 | Manuel Franceschini | success = (not live_port_needed) and (errcode == errno.ECONNREFUSED) |
278 | a744b676 | Manuel Franceschini | |
279 | a744b676 | Manuel Franceschini | return success
|
280 | a744b676 | Manuel Franceschini | |
281 | a744b676 | Manuel Franceschini | |
282 | a744b676 | Manuel Franceschini | def OwnIpAddress(address): |
283 | a744b676 | Manuel Franceschini | """Check if the current host has the the given IP address.
|
284 | a744b676 | Manuel Franceschini |
|
285 | a744b676 | Manuel Franceschini | This is done by trying to bind the given address. We return True if we
|
286 | a744b676 | Manuel Franceschini | succeed or false if a socket.error is raised.
|
287 | a744b676 | Manuel Franceschini |
|
288 | a744b676 | Manuel Franceschini | @type address: string
|
289 | a744b676 | Manuel Franceschini | @param address: the address to check
|
290 | a744b676 | Manuel Franceschini | @rtype: bool
|
291 | a744b676 | Manuel Franceschini | @return: True if we own the address
|
292 | a744b676 | Manuel Franceschini |
|
293 | a744b676 | Manuel Franceschini | """
|
294 | a744b676 | Manuel Franceschini | family = GetAddressFamily(address) |
295 | a744b676 | Manuel Franceschini | s = socket.socket(family, socket.SOCK_DGRAM) |
296 | a744b676 | Manuel Franceschini | success = False
|
297 | a744b676 | Manuel Franceschini | try:
|
298 | a744b676 | Manuel Franceschini | try:
|
299 | a744b676 | Manuel Franceschini | s.bind((address, 0))
|
300 | a744b676 | Manuel Franceschini | success = True
|
301 | a744b676 | Manuel Franceschini | except socket.error:
|
302 | a744b676 | Manuel Franceschini | success = False
|
303 | a744b676 | Manuel Franceschini | finally:
|
304 | a744b676 | Manuel Franceschini | s.close() |
305 | a744b676 | Manuel Franceschini | return success
|
306 | a744b676 | Manuel Franceschini | |
307 | a744b676 | Manuel Franceschini | |
308 | a744b676 | Manuel Franceschini | def GetDaemonPort(daemon_name): |
309 | a744b676 | Manuel Franceschini | """Get the daemon port for this cluster.
|
310 | a744b676 | Manuel Franceschini |
|
311 | a744b676 | Manuel Franceschini | Note that this routine does not read a ganeti-specific file, but
|
312 | a744b676 | Manuel Franceschini | instead uses C{socket.getservbyname} to allow pre-customization of
|
313 | a744b676 | Manuel Franceschini | this parameter outside of Ganeti.
|
314 | a744b676 | Manuel Franceschini |
|
315 | a744b676 | Manuel Franceschini | @type daemon_name: string
|
316 | a744b676 | Manuel Franceschini | @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
|
317 | a744b676 | Manuel Franceschini | @rtype: int
|
318 | a744b676 | Manuel Franceschini |
|
319 | a744b676 | Manuel Franceschini | """
|
320 | a744b676 | Manuel Franceschini | if daemon_name not in constants.DAEMONS_PORTS: |
321 | a744b676 | Manuel Franceschini | raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name) |
322 | a744b676 | Manuel Franceschini | |
323 | a744b676 | Manuel Franceschini | (proto, default_port) = constants.DAEMONS_PORTS[daemon_name] |
324 | a744b676 | Manuel Franceschini | try:
|
325 | a744b676 | Manuel Franceschini | port = socket.getservbyname(daemon_name, proto) |
326 | a744b676 | Manuel Franceschini | except socket.error:
|
327 | a744b676 | Manuel Franceschini | port = default_port |
328 | a744b676 | Manuel Franceschini | |
329 | a744b676 | Manuel Franceschini | return port |