4 # Copyright (C) 2010 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Ganeti network utility module.
24 This module holds functions that can be used in both daemons (all) and
25 the command line scripts.
36 from ganeti import constants
37 from ganeti import errors
39 # Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
40 # struct ucred { pid_t pid; uid_t uid; gid_t gid; };
42 # The GNU C Library defines gid_t and uid_t to be "unsigned int" and
45 # IEEE Std 1003.1-2008:
46 # "nlink_t, uid_t, gid_t, and id_t shall be integer types"
47 # "blksize_t, pid_t, and ssize_t shall be signed integer types"
49 _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
52 def GetSocketCredentials(sock):
53 """Returns the credentials of the foreign process connected to a socket.
55 @param sock: Unix socket
56 @rtype: tuple; (number, number, number)
57 @return: The PID, UID and GID of the connected foreign process.
60 peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
62 return struct.unpack(_STRUCT_UCRED, peercred)
65 def GetHostInfo(name=None):
66 """Lookup host name and raise an OpPrereqError for failures"""
70 except errors.ResolverError, err:
71 raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
72 (err[0], err[2]), errors.ECODE_RESOLVER)
76 """Class implementing resolver and hostname functionality
79 _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
81 def __init__(self, name=None):
82 """Initialize the host name object.
84 If the name argument is not passed, it will use this system's
92 self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
93 self.ip = self.ipaddrs[0]
96 """Returns the hostname without domain.
99 return self.name.split('.')[0]
103 """Return the current system's name.
105 This is simply a wrapper over C{socket.gethostname()}.
108 return socket.gethostname()
111 def LookupHostname(hostname):
115 @param hostname: hostname to look up
118 @return: a tuple (name, aliases, ipaddrs) as returned by
119 C{socket.gethostbyname_ex}
120 @raise errors.ResolverError: in case of errors in resolving
124 result = socket.gethostbyname_ex(hostname)
125 except (socket.gaierror, socket.herror, socket.error), err:
126 # hostname not found in DNS, or other socket exception in the
127 # (code, description format)
128 raise errors.ResolverError(hostname, err.args[0], err.args[1])
133 def NormalizeName(cls, hostname):
134 """Validate and normalize the given hostname.
136 @attention: the validation is a bit more relaxed than the standards
137 require; most importantly, we allow underscores in names
138 @raise errors.OpPrereqError: when the name is not valid
141 hostname = hostname.lower()
142 if (not cls._VALID_NAME_RE.match(hostname) or
143 # double-dots, meaning empty label
145 # empty initial label
146 hostname.startswith(".")):
147 raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
149 if hostname.endswith("."):
150 hostname = hostname.rstrip(".")
154 def _GenericIsValidIP(family, ip):
155 """Generic internal version of ip validation.
158 @param family: socket.AF_INET | socket.AF_INET6
160 @param ip: the address to be checked
162 @return: True if ip is valid, False otherwise
166 socket.inet_pton(family, ip)
173 """Verifies an IPv4 address.
175 This function checks if the given address is a valid IPv4 address.
178 @param ip: the address to be checked
180 @return: True if ip is valid, False otherwise
183 return _GenericIsValidIP(socket.AF_INET, ip)
187 """Verifies an IPv6 address.
189 This function checks if the given address is a valid IPv6 address.
192 @param ip: the address to be checked
194 @return: True if ip is valid, False otherwise
197 return _GenericIsValidIP(socket.AF_INET6, ip)
201 """Verifies an IP address.
203 This function checks if the given IP address (both IPv4 and IPv6) is valid.
206 @param ip: the address to be checked
208 @return: True if ip is valid, False otherwise
211 return IsValidIP4(ip) or IsValidIP6(ip)
214 def GetAddressFamily(ip):
215 """Get the address family of the given address.
218 @param ip: ip address whose family will be returned
220 @return: socket.AF_INET or socket.AF_INET6
221 @raise errors.GenericError: for invalid addresses
225 return socket.AF_INET6
227 return socket.AF_INET
229 raise errors.GenericError("Address %s not valid" % ip)
232 def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
233 """Simple ping implementation using TCP connect(2).
235 Check if the given IP is reachable by doing attempting a TCP connect
239 @param target: the IP or hostname to ping
241 @param port: the port to connect to
243 @param timeout: the timeout on the connection attempt
244 @type live_port_needed: boolean
245 @param live_port_needed: whether a closed port will cause the
246 function to return failure, as if there was a timeout
247 @type source: str or None
248 @param source: if specified, will cause the connect to be made
249 from this specific source address; failures to bind other
250 than C{EADDRNOTAVAIL} will be ignored
254 family = GetAddressFamily(target)
255 except errors.GenericError:
258 sock = socket.socket(family, socket.SOCK_STREAM)
261 if source is not None:
263 sock.bind((source, 0))
264 except socket.error, (errcode, _):
265 if errcode == errno.EADDRNOTAVAIL:
268 sock.settimeout(timeout)
271 sock.connect((target, port))
274 except socket.timeout:
276 except socket.error, (errcode, _):
277 success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
282 def OwnIpAddress(address):
283 """Check if the current host has the the given IP address.
285 This is done by trying to bind the given address. We return True if we
286 succeed or false if a socket.error is raised.
288 @type address: string
289 @param address: the address to check
291 @return: True if we own the address
294 family = GetAddressFamily(address)
295 s = socket.socket(family, socket.SOCK_DGRAM)
308 def GetDaemonPort(daemon_name):
309 """Get the daemon port for this cluster.
311 Note that this routine does not read a ganeti-specific file, but
312 instead uses C{socket.getservbyname} to allow pre-customization of
313 this parameter outside of Ganeti.
315 @type daemon_name: string
316 @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
320 if daemon_name not in constants.DAEMONS_PORTS:
321 raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
323 (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
325 port = socket.getservbyname(daemon_name, proto)