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 # Workaround a bug in some linux distributions that don't define SO_PEERCRED
57 # pylint: disable=E1101
58 _SO_PEERCRED = IN.SO_PEERCRED
59 except AttributeError:
62 # Regexes used to find IP addresses in the output of ip.
63 _IP_RE_TEXT = r"[.:a-z0-9]+" # separate for testing purposes
64 _IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT,
67 # Dict used to convert from a string representing an IP family to an IP
70 "inet": constants.IP4_VERSION,
71 "inet6": constants.IP6_VERSION,
75 def _GetIpAddressesFromIpOutput(ip_output):
76 """Parses the output of the ip command and retrieves the IP addresses and
79 @param ip_output: string containing the output of the ip command;
80 @rtype: dict; (int, list)
81 @return: a dict having as keys the IP versions and as values the
82 corresponding list of addresses found in the IP output.
85 addr = dict((i, []) for i in _NAME_TO_IP_VER.values())
87 for row in ip_output.splitlines():
88 match = _IP_FAMILY_RE.search(row)
89 if match and IPAddress.IsValid(match.group("ip")):
90 addr[_NAME_TO_IP_VER[match.group("family")]].append(match.group("ip"))
95 def GetSocketCredentials(sock):
96 """Returns the credentials of the foreign process connected to a socket.
98 @param sock: Unix socket
99 @rtype: tuple; (number, number, number)
100 @return: The PID, UID and GID of the connected foreign process.
103 peercred = sock.getsockopt(socket.SOL_SOCKET, _SO_PEERCRED,
105 return struct.unpack(_STRUCT_UCRED, peercred)
108 def IsValidInterface(ifname):
109 """Validate an interface name.
112 @param ifname: Name of the network interface
113 @return: boolean indicating whether the interface name is valid or not.
116 return os.path.exists(utils.PathJoin("/sys/class/net", ifname))
119 def GetInterfaceIpAddresses(ifname):
120 """Returns the IP addresses associated to the interface.
123 @param ifname: Name of the network interface
124 @return: A dict having for keys the IP version (either
125 L{constants.IP4_VERSION} or L{constants.IP6_VERSION}) and for
126 values the lists of IP addresses of the respective version
127 associated to the interface
130 result = utils.RunCmd([constants.IP_COMMAND_PATH, "-o", "addr", "show",
134 logging.error("Error running the ip command while getting the IP"
135 " addresses of %s", ifname)
138 return _GetIpAddressesFromIpOutput(result.output)
141 def GetHostname(name=None, family=None):
142 """Returns a Hostname object.
145 @param name: hostname or None
147 @param family: AF_INET | AF_INET6 | None
149 @return: Hostname object
150 @raise errors.OpPrereqError: in case of errors in resolving
154 return Hostname(name=name, family=family)
155 except errors.ResolverError, err:
156 raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
157 (err[0], err[2]), errors.ECODE_RESOLVER)
161 """Class implementing resolver and hostname functionality.
164 _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
166 def __init__(self, name=None, family=None):
167 """Initialize the host name object.
169 If the name argument is None, it will use this system's name.
172 @param family: AF_INET | AF_INET6 | None
174 @param name: hostname or None
177 self.name = self.GetFqdn(name)
178 self.ip = self.GetIP(self.name, family=family)
182 """Legacy method the get the current system's name.
188 def GetFqdn(cls, hostname=None):
191 If hostname is None the system's fqdn is returned.
194 @param hostname: name to be fqdn'ed
196 @return: fqdn of given name, if it exists, unmodified name otherwise
200 virtfqdn = vcluster.GetVirtualHostname()
204 result = socket.getfqdn()
206 result = socket.getfqdn(hostname)
208 return cls.GetNormalizedName(result)
211 def GetIP(hostname, family=None):
212 """Return IP address of given hostname.
214 Supports both IPv4 and IPv6.
217 @param hostname: hostname to look up
219 @param family: AF_INET | AF_INET6 | None
222 @raise errors.ResolverError: in case of errors in resolving
226 if family in (socket.AF_INET, socket.AF_INET6):
227 result = socket.getaddrinfo(hostname, None, family)
229 result = socket.getaddrinfo(hostname, None)
230 except (socket.gaierror, socket.herror, socket.error), err:
231 # hostname not found in DNS, or other socket exception in the
232 # (code, description format)
233 raise errors.ResolverError(hostname, err.args[0], err.args[1])
235 # getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
236 # canonname, sockaddr). We return the first tuple's first address in
239 return result[0][4][0]
240 except IndexError, err:
241 # we don't have here an actual error code, it's just that the
242 # data type returned by getaddrinfo is not what we expected;
243 # let's keep the same format in the exception arguments with a
245 raise errors.ResolverError(hostname, 0,
246 "Unknown error in getaddrinfo(): %s" % err)
249 def GetNormalizedName(cls, hostname):
250 """Validate and normalize the given hostname.
252 @attention: the validation is a bit more relaxed than the standards
253 require; most importantly, we allow underscores in names
254 @raise errors.OpPrereqError: when the name is not valid
257 hostname = hostname.lower()
258 if (not cls._VALID_NAME_RE.match(hostname) or
259 # double-dots, meaning empty label
261 # empty initial label
262 hostname.startswith(".")):
263 raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
265 if hostname.endswith("."):
266 hostname = hostname.rstrip(".")
270 def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
271 """Simple ping implementation using TCP connect(2).
273 Check if the given IP is reachable by doing attempting a TCP connect
277 @param target: the IP to ping
279 @param port: the port to connect to
281 @param timeout: the timeout on the connection attempt
282 @type live_port_needed: boolean
283 @param live_port_needed: whether a closed port will cause the
284 function to return failure, as if there was a timeout
285 @type source: str or None
286 @param source: if specified, will cause the connect to be made
287 from this specific source address; failures to bind other
288 than C{EADDRNOTAVAIL} will be ignored
291 logging.debug("Attempting to reach TCP port %s on target %s with a timeout"
292 " of %s seconds", port, target, timeout)
295 family = IPAddress.GetAddressFamily(target)
296 except errors.IPAddressError, err:
297 raise errors.ProgrammerError("Family of IP address given in parameter"
298 " 'target' can't be determined: %s" % err)
300 sock = socket.socket(family, socket.SOCK_STREAM)
303 if source is not None:
305 sock.bind((source, 0))
306 except socket.error, err:
307 if err[0] == errno.EADDRNOTAVAIL:
310 sock.settimeout(timeout)
313 sock.connect((target, port))
316 except socket.timeout:
318 except socket.error, err:
319 success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED)
324 def GetDaemonPort(daemon_name):
325 """Get the daemon port for this cluster.
327 Note that this routine does not read a ganeti-specific file, but
328 instead uses C{socket.getservbyname} to allow pre-customization of
329 this parameter outside of Ganeti.
331 @type daemon_name: string
332 @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
336 if daemon_name not in constants.DAEMONS_PORTS:
337 raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
339 (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
341 port = socket.getservbyname(daemon_name, proto)
348 class IPAddress(object):
349 """Class that represents an IP address.
357 def _GetIPIntFromString(address):
358 """Abstract method to please pylint.
361 raise NotImplementedError
364 def IsValid(cls, address):
365 """Validate a IP address.
368 @param address: IP address to be checked
370 @return: True if valid, False otherwise
373 if cls.family is None:
375 family = cls.GetAddressFamily(address)
376 except errors.IPAddressError:
382 socket.inet_pton(family, address)
388 def ValidateNetmask(cls, netmask):
389 """Validate a netmask suffix in CIDR notation.
392 @param netmask: netmask suffix to validate
394 @return: True if valid, False otherwise
397 assert (isinstance(netmask, (int, long)))
399 return 0 < netmask <= cls.iplen
402 def Own(cls, address):
403 """Check if the current host has the the given IP address.
405 This is done by trying to bind the given address. We return True if we
406 succeed or false if a socket.error is raised.
409 @param address: IP address to be checked
411 @return: True if we own the address, False otherwise
414 if cls.family is None:
416 family = cls.GetAddressFamily(address)
417 except errors.IPAddressError:
422 s = socket.socket(family, socket.SOCK_DGRAM)
435 def InNetwork(cls, cidr, address):
436 """Determine whether an address is within a network.
439 @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
441 @param address: IP address
443 @return: True if address is in cidr, False otherwise
446 address_int = cls._GetIPIntFromString(address)
447 subnet = cidr.split("/")
448 assert len(subnet) == 2
450 prefix = int(subnet[1])
454 assert 0 <= prefix <= cls.iplen
455 target_int = cls._GetIPIntFromString(subnet[0])
456 # Convert prefix netmask to integer value of netmask
457 netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
459 hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
460 # Calculate network address by and'ing netmask
461 network_int = target_int & netmask_int
462 # Calculate broadcast address by or'ing hostmask
463 broadcast_int = target_int | hostmask_int
465 return network_int <= address_int <= broadcast_int
468 def GetAddressFamily(address):
469 """Get the address family of the given address.
472 @param address: ip address whose family will be returned
474 @return: C{socket.AF_INET} or C{socket.AF_INET6}
475 @raise errors.GenericError: for invalid addresses
479 return IP4Address(address).family
480 except errors.IPAddressError:
484 return IP6Address(address).family
485 except errors.IPAddressError:
488 raise errors.IPAddressError("Invalid address '%s'" % address)
491 def GetVersionFromAddressFamily(family):
492 """Convert an IP address family to the corresponding IP version.
495 @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
496 @return: an int containing the IP version, one of L{constants.IP4_VERSION}
497 or L{constants.IP6_VERSION}
498 @raise errors.ProgrammerError: for unknown families
501 if family == socket.AF_INET:
502 return constants.IP4_VERSION
503 elif family == socket.AF_INET6:
504 return constants.IP6_VERSION
506 raise errors.ProgrammerError("%s is not a valid IP address family" % family)
509 def GetAddressFamilyFromVersion(version):
510 """Convert an IP version to the corresponding IP address family.
513 @param version: IP version, one of L{constants.IP4_VERSION} or
514 L{constants.IP6_VERSION}
515 @return: an int containing the IP address family, one of C{socket.AF_INET}
516 or C{socket.AF_INET6}
517 @raise errors.ProgrammerError: for unknown IP versions
520 if version == constants.IP4_VERSION:
521 return socket.AF_INET
522 elif version == constants.IP6_VERSION:
523 return socket.AF_INET6
525 raise errors.ProgrammerError("%s is not a valid IP version" % version)
528 def GetClassFromIpVersion(version):
529 """Return the IPAddress subclass for the given IP version.
532 @param version: IP version, one of L{constants.IP4_VERSION} or
533 L{constants.IP6_VERSION}
534 @return: a subclass of L{netutils.IPAddress}
535 @raise errors.ProgrammerError: for unknowo IP versions
538 if version == constants.IP4_VERSION:
540 elif version == constants.IP6_VERSION:
543 raise errors.ProgrammerError("%s is not a valid IP version" % version)
546 def GetClassFromIpFamily(family):
547 """Return the IPAddress subclass for the given IP family.
549 @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6}
550 @return: a subclass of L{netutils.IPAddress}
551 @raise errors.ProgrammerError: for unknowo IP versions
554 return IPAddress.GetClassFromIpVersion(
555 IPAddress.GetVersionFromAddressFamily(family))
558 def IsLoopback(cls, address):
559 """Determine whether it is a loopback address.
562 @param address: IP address to be checked
564 @return: True if loopback, False otherwise
568 return cls.InNetwork(cls.loopback_cidr, address)
569 except errors.IPAddressError:
573 class IP4Address(IPAddress):
574 """IPv4 address class.
578 family = socket.AF_INET
579 loopback_cidr = "127.0.0.0/8"
581 def __init__(self, address):
582 """Constructor for IPv4 address.
585 @param address: IP address
586 @raises errors.IPAddressError: if address invalid
589 IPAddress.__init__(self)
590 if not self.IsValid(address):
591 raise errors.IPAddressError("IPv4 Address %s invalid" % address)
593 self.address = address
596 def _GetIPIntFromString(address):
597 """Get integer value of IPv4 address.
600 @param address: IPv6 address
602 @return: integer value of given IP address
606 parts = address.split(".")
607 assert len(parts) == 4
609 address_int = (address_int << 8) | int(part)
614 class IP6Address(IPAddress):
615 """IPv6 address class.
619 family = socket.AF_INET6
620 loopback_cidr = "::1/128"
622 def __init__(self, address):
623 """Constructor for IPv6 address.
626 @param address: IP address
627 @raises errors.IPAddressError: if address invalid
630 IPAddress.__init__(self)
631 if not self.IsValid(address):
632 raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
633 self.address = address
636 def _GetIPIntFromString(address):
637 """Get integer value of IPv6 address.
640 @param address: IPv6 address
642 @return: integer value of given IP address
645 doublecolons = address.count("::")
646 assert not doublecolons > 1
647 if doublecolons == 1:
648 # We have a shorthand address, expand it
650 twoparts = address.split("::")
651 sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
652 parts = twoparts[0].split(":")
653 parts.extend(["0"] * (8 - sep))
654 parts += twoparts[1].split(":")
656 parts = address.split(":")
660 address_int = (address_int << 16) + int(part or "0", 16)
665 def FormatAddress(address, family=None):
666 """Format a socket address
668 @type address: family specific (usually tuple)
669 @param address: address, as reported by this class
670 @type family: integer
671 @param family: socket family (one of socket.AF_*) or None
676 family = IPAddress.GetAddressFamily(address[0])
677 except errors.IPAddressError:
678 raise errors.ParameterError(address)
680 if family == socket.AF_UNIX and len(address) == 3:
681 return "pid=%s, uid=%s, gid=%s" % address
683 if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
685 if family == socket.AF_INET6:
695 raise errors.ParameterError(family, address)