4 # Copyright (C) 2010, 2011, 2012 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.
38 from ganeti import constants
39 from ganeti import errors
40 from ganeti import utils
41 from ganeti import vcluster
43 # Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
44 # struct ucred { pid_t pid; uid_t uid; gid_t gid; };
46 # The GNU C Library defines gid_t and uid_t to be "unsigned int" and
49 # IEEE Std 1003.1-2008:
50 # "nlink_t, uid_t, gid_t, and id_t shall be integer types"
51 # "blksize_t, pid_t, and ssize_t shall be signed integer types"
53 _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
55 # Regexes used to find IP addresses in the output of ip.
56 _IP_RE_TEXT = r"[.:a-z0-9]+" # separate for testing purposes
57 _IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT,
60 # Dict used to convert from a string representing an IP family to an IP
63 "inet": constants.IP4_VERSION,
64 "inet6": constants.IP6_VERSION,
68 def _GetIpAddressesFromIpOutput(ip_output):
69 """Parses the output of the ip command and retrieves the IP addresses and
72 @param ip_output: string containing the output of the ip command;
73 @rtype: dict; (int, list)
74 @return: a dict having as keys the IP versions and as values the
75 corresponding list of addresses found in the IP output.
78 addr = dict((i, []) for i in _NAME_TO_IP_VER.values())
80 for row in ip_output.splitlines():
81 match = _IP_FAMILY_RE.search(row)
82 if match and IPAddress.IsValid(match.group("ip")):
83 addr[_NAME_TO_IP_VER[match.group("family")]].append(match.group("ip"))
88 def GetSocketCredentials(sock):
89 """Returns the credentials of the foreign process connected to a socket.
91 @param sock: Unix socket
92 @rtype: tuple; (number, number, number)
93 @return: The PID, UID and GID of the connected foreign process.
96 peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
98 return struct.unpack(_STRUCT_UCRED, peercred)
101 def IsValidInterface(ifname):
102 """Validate an interface name.
105 @param ifname: Name of the network interface
106 @return: boolean indicating whether the interface name is valid or not.
109 return os.path.exists(utils.PathJoin("/sys/class/net", ifname))
112 def GetInterfaceIpAddresses(ifname):
113 """Returns the IP addresses associated to the interface.
116 @param ifname: Name of the network interface
117 @return: A dict having for keys the IP version (either
118 L{constants.IP4_VERSION} or L{constants.IP6_VERSION}) and for
119 values the lists of IP addresses of the respective version
120 associated to the interface
123 result = utils.RunCmd([constants.IP_COMMAND_PATH, "-o", "addr", "show",
127 logging.error("Error running the ip command while getting the IP"
128 " addresses of %s", ifname)
131 return _GetIpAddressesFromIpOutput(result.output)
134 def GetHostname(name=None, family=None):
135 """Returns a Hostname object.
138 @param name: hostname or None
140 @param family: AF_INET | AF_INET6 | None
142 @return: Hostname object
143 @raise errors.OpPrereqError: in case of errors in resolving
147 return Hostname(name=name, family=family)
148 except errors.ResolverError, err:
149 raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
150 (err[0], err[2]), errors.ECODE_RESOLVER)
154 """Class implementing resolver and hostname functionality.
157 _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
159 def __init__(self, name=None, family=None):
160 """Initialize the host name object.
162 If the name argument is None, it will use this system's name.
165 @param family: AF_INET | AF_INET6 | None
167 @param name: hostname or None
170 self.name = self.GetFqdn(name)
171 self.ip = self.GetIP(self.name, family=family)
175 """Legacy method the get the current system's name.
181 def GetFqdn(cls, hostname=None):
184 If hostname is None the system's fqdn is returned.
187 @param hostname: name to be fqdn'ed
189 @return: fqdn of given name, if it exists, unmodified name otherwise
193 virtfqdn = vcluster.GetVirtualHostname()
197 result = socket.getfqdn()
199 result = socket.getfqdn(hostname)
201 return cls.GetNormalizedName(result)
204 def GetIP(hostname, family=None):
205 """Return IP address of given hostname.
207 Supports both IPv4 and IPv6.
210 @param hostname: hostname to look up
212 @param family: AF_INET | AF_INET6 | None
215 @raise errors.ResolverError: in case of errors in resolving
219 if family in (socket.AF_INET, socket.AF_INET6):
220 result = socket.getaddrinfo(hostname, None, family)
222 result = socket.getaddrinfo(hostname, None)
223 except (socket.gaierror, socket.herror, socket.error), err:
224 # hostname not found in DNS, or other socket exception in the
225 # (code, description format)
226 raise errors.ResolverError(hostname, err.args[0], err.args[1])
228 # getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
229 # canonname, sockaddr). We return the first tuple's first address in
232 return result[0][4][0]
233 except IndexError, err:
234 # we don't have here an actual error code, it's just that the
235 # data type returned by getaddrinfo is not what we expected;
236 # let's keep the same format in the exception arguments with a
238 raise errors.ResolverError(hostname, 0,
239 "Unknown error in getaddrinfo(): %s" % err)
242 def GetNormalizedName(cls, hostname):
243 """Validate and normalize the given hostname.
245 @attention: the validation is a bit more relaxed than the standards
246 require; most importantly, we allow underscores in names
247 @raise errors.OpPrereqError: when the name is not valid
250 hostname = hostname.lower()
251 if (not cls._VALID_NAME_RE.match(hostname) or
252 # double-dots, meaning empty label
254 # empty initial label
255 hostname.startswith(".")):
256 raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
258 if hostname.endswith("."):
259 hostname = hostname.rstrip(".")
263 def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
264 """Simple ping implementation using TCP connect(2).
266 Check if the given IP is reachable by doing attempting a TCP connect
270 @param target: the IP to ping
272 @param port: the port to connect to
274 @param timeout: the timeout on the connection attempt
275 @type live_port_needed: boolean
276 @param live_port_needed: whether a closed port will cause the
277 function to return failure, as if there was a timeout
278 @type source: str or None
279 @param source: if specified, will cause the connect to be made
280 from this specific source address; failures to bind other
281 than C{EADDRNOTAVAIL} will be ignored
285 family = IPAddress.GetAddressFamily(target)
286 except errors.GenericError:
289 sock = socket.socket(family, socket.SOCK_STREAM)
292 if source is not None:
294 sock.bind((source, 0))
295 except socket.error, err:
296 if err[0] == errno.EADDRNOTAVAIL:
299 sock.settimeout(timeout)
302 sock.connect((target, port))
305 except socket.timeout:
307 except socket.error, err:
308 success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED)
313 def GetDaemonPort(daemon_name):
314 """Get the daemon port for this cluster.
316 Note that this routine does not read a ganeti-specific file, but
317 instead uses C{socket.getservbyname} to allow pre-customization of
318 this parameter outside of Ganeti.
320 @type daemon_name: string
321 @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
325 if daemon_name not in constants.DAEMONS_PORTS:
326 raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
328 (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
330 port = socket.getservbyname(daemon_name, proto)
337 class IPAddress(object):
338 """Class that represents an IP address.
346 def _GetIPIntFromString(address):
347 """Abstract method to please pylint.
350 raise NotImplementedError
353 def IsValid(cls, address):
354 """Validate a IP address.
357 @param address: IP address to be checked
359 @return: True if valid, False otherwise
362 if cls.family is None:
364 family = cls.GetAddressFamily(address)
365 except errors.IPAddressError:
371 socket.inet_pton(family, address)
377 def ValidateNetmask(cls, netmask):
378 """Validate a netmask suffix in CIDR notation.
381 @param netmask: netmask suffix to validate
383 @return: True if valid, False otherwise
386 assert (isinstance(netmask, (int, long)))
388 return 0 < netmask <= cls.iplen
391 def Own(cls, address):
392 """Check if the current host has the the given IP address.
394 This is done by trying to bind the given address. We return True if we
395 succeed or false if a socket.error is raised.
398 @param address: IP address to be checked
400 @return: True if we own the address, False otherwise
403 if cls.family is None:
405 family = cls.GetAddressFamily(address)
406 except errors.IPAddressError:
411 s = socket.socket(family, socket.SOCK_DGRAM)
424 def InNetwork(cls, cidr, address):
425 """Determine whether an address is within a network.
428 @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
430 @param address: IP address
432 @return: True if address is in cidr, False otherwise
435 address_int = cls._GetIPIntFromString(address)
436 subnet = cidr.split("/")
437 assert len(subnet) == 2
439 prefix = int(subnet[1])
443 assert 0 <= prefix <= cls.iplen
444 target_int = cls._GetIPIntFromString(subnet[0])
445 # Convert prefix netmask to integer value of netmask
446 netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
448 hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
449 # Calculate network address by and'ing netmask
450 network_int = target_int & netmask_int
451 # Calculate broadcast address by or'ing hostmask
452 broadcast_int = target_int | hostmask_int
454 return network_int <= address_int <= broadcast_int
457 def GetAddressFamily(address):
458 """Get the address family of the given address.
461 @param address: ip address whose family will be returned
463 @return: C{socket.AF_INET} or C{socket.AF_INET6}
464 @raise errors.GenericError: for invalid addresses
468 return IP4Address(address).family
469 except errors.IPAddressError:
473 return IP6Address(address).family
474 except errors.IPAddressError:
477 raise errors.IPAddressError("Invalid address '%s'" % address)
480 def GetVersionFromAddressFamily(family):
481 """Convert an IP address family to the corresponding IP version.
484 @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
485 @return: an int containing the IP version, one of L{constants.IP4_VERSION}
486 or L{constants.IP6_VERSION}
487 @raise errors.ProgrammerError: for unknown families
490 if family == socket.AF_INET:
491 return constants.IP4_VERSION
492 elif family == socket.AF_INET6:
493 return constants.IP6_VERSION
495 raise errors.ProgrammerError("%s is not a valid IP address family" % family)
498 def GetAddressFamilyFromVersion(version):
499 """Convert an IP version to the corresponding IP address family.
502 @param version: IP version, one of L{constants.IP4_VERSION} or
503 L{constants.IP6_VERSION}
504 @return: an int containing the IP address family, one of C{socket.AF_INET}
505 or C{socket.AF_INET6}
506 @raise errors.ProgrammerError: for unknown IP versions
509 if version == constants.IP4_VERSION:
510 return socket.AF_INET
511 elif version == constants.IP6_VERSION:
512 return socket.AF_INET6
514 raise errors.ProgrammerError("%s is not a valid IP version" % version)
517 def GetClassFromIpVersion(version):
518 """Return the IPAddress subclass for the given IP version.
521 @param version: IP version, one of L{constants.IP4_VERSION} or
522 L{constants.IP6_VERSION}
523 @return: a subclass of L{netutils.IPAddress}
524 @raise errors.ProgrammerError: for unknowo IP versions
527 if version == constants.IP4_VERSION:
529 elif version == constants.IP6_VERSION:
532 raise errors.ProgrammerError("%s is not a valid IP version" % version)
535 def GetClassFromIpFamily(family):
536 """Return the IPAddress subclass for the given IP family.
538 @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6}
539 @return: a subclass of L{netutils.IPAddress}
540 @raise errors.ProgrammerError: for unknowo IP versions
543 return IPAddress.GetClassFromIpVersion(
544 IPAddress.GetVersionFromAddressFamily(family))
547 def IsLoopback(cls, address):
548 """Determine whether it is a loopback address.
551 @param address: IP address to be checked
553 @return: True if loopback, False otherwise
557 return cls.InNetwork(cls.loopback_cidr, address)
558 except errors.IPAddressError:
562 class IP4Address(IPAddress):
563 """IPv4 address class.
567 family = socket.AF_INET
568 loopback_cidr = "127.0.0.0/8"
570 def __init__(self, address):
571 """Constructor for IPv4 address.
574 @param address: IP address
575 @raises errors.IPAddressError: if address invalid
578 IPAddress.__init__(self)
579 if not self.IsValid(address):
580 raise errors.IPAddressError("IPv4 Address %s invalid" % address)
582 self.address = address
585 def _GetIPIntFromString(address):
586 """Get integer value of IPv4 address.
589 @param address: IPv6 address
591 @return: integer value of given IP address
595 parts = address.split(".")
596 assert len(parts) == 4
598 address_int = (address_int << 8) | int(part)
603 class IP6Address(IPAddress):
604 """IPv6 address class.
608 family = socket.AF_INET6
609 loopback_cidr = "::1/128"
611 def __init__(self, address):
612 """Constructor for IPv6 address.
615 @param address: IP address
616 @raises errors.IPAddressError: if address invalid
619 IPAddress.__init__(self)
620 if not self.IsValid(address):
621 raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
622 self.address = address
625 def _GetIPIntFromString(address):
626 """Get integer value of IPv6 address.
629 @param address: IPv6 address
631 @return: integer value of given IP address
634 doublecolons = address.count("::")
635 assert not doublecolons > 1
636 if doublecolons == 1:
637 # We have a shorthand address, expand it
639 twoparts = address.split("::")
640 sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
641 parts = twoparts[0].split(":")
642 parts.extend(["0"] * (8 - sep))
643 parts += twoparts[1].split(":")
645 parts = address.split(":")
649 address_int = (address_int << 16) + int(part or "0", 16)
654 def FormatAddress(address, family=None):
655 """Format a socket address
657 @type address: family specific (usually tuple)
658 @param address: address, as reported by this class
659 @type family: integer
660 @param family: socket family (one of socket.AF_*) or None
665 family = IPAddress.GetAddressFamily(address[0])
666 except errors.IPAddressError:
667 raise errors.ParameterError(address)
669 if family == socket.AF_UNIX and len(address) == 3:
670 return "pid=%s, uid=%s, gid=%s" % address
672 if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
674 if family == socket.AF_INET6:
684 raise errors.ParameterError(family, address)