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
284 logging.debug("Attempting to reach TCP port %s on target %s with a timeout"
285 " of %s seconds", port, target, timeout)
288 family = IPAddress.GetAddressFamily(target)
289 except errors.IPAddressError, err:
290 raise errors.ProgrammerError("Family of IP address given in parameter"
291 " 'target' can't be determined: %s" % err)
293 sock = socket.socket(family, socket.SOCK_STREAM)
296 if source is not None:
298 sock.bind((source, 0))
299 except socket.error, err:
300 if err[0] == errno.EADDRNOTAVAIL:
303 sock.settimeout(timeout)
306 sock.connect((target, port))
309 except socket.timeout:
311 except socket.error, err:
312 success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED)
317 def GetDaemonPort(daemon_name):
318 """Get the daemon port for this cluster.
320 Note that this routine does not read a ganeti-specific file, but
321 instead uses C{socket.getservbyname} to allow pre-customization of
322 this parameter outside of Ganeti.
324 @type daemon_name: string
325 @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
329 if daemon_name not in constants.DAEMONS_PORTS:
330 raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
332 (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
334 port = socket.getservbyname(daemon_name, proto)
341 class IPAddress(object):
342 """Class that represents an IP address.
350 def _GetIPIntFromString(address):
351 """Abstract method to please pylint.
354 raise NotImplementedError
357 def IsValid(cls, address):
358 """Validate a IP address.
361 @param address: IP address to be checked
363 @return: True if valid, False otherwise
366 if cls.family is None:
368 family = cls.GetAddressFamily(address)
369 except errors.IPAddressError:
375 socket.inet_pton(family, address)
381 def ValidateNetmask(cls, netmask):
382 """Validate a netmask suffix in CIDR notation.
385 @param netmask: netmask suffix to validate
387 @return: True if valid, False otherwise
390 assert (isinstance(netmask, (int, long)))
392 return 0 < netmask <= cls.iplen
395 def Own(cls, address):
396 """Check if the current host has the the given IP address.
398 This is done by trying to bind the given address. We return True if we
399 succeed or false if a socket.error is raised.
402 @param address: IP address to be checked
404 @return: True if we own the address, False otherwise
407 if cls.family is None:
409 family = cls.GetAddressFamily(address)
410 except errors.IPAddressError:
415 s = socket.socket(family, socket.SOCK_DGRAM)
428 def InNetwork(cls, cidr, address):
429 """Determine whether an address is within a network.
432 @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
434 @param address: IP address
436 @return: True if address is in cidr, False otherwise
439 address_int = cls._GetIPIntFromString(address)
440 subnet = cidr.split("/")
441 assert len(subnet) == 2
443 prefix = int(subnet[1])
447 assert 0 <= prefix <= cls.iplen
448 target_int = cls._GetIPIntFromString(subnet[0])
449 # Convert prefix netmask to integer value of netmask
450 netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
452 hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
453 # Calculate network address by and'ing netmask
454 network_int = target_int & netmask_int
455 # Calculate broadcast address by or'ing hostmask
456 broadcast_int = target_int | hostmask_int
458 return network_int <= address_int <= broadcast_int
461 def GetAddressFamily(address):
462 """Get the address family of the given address.
465 @param address: ip address whose family will be returned
467 @return: C{socket.AF_INET} or C{socket.AF_INET6}
468 @raise errors.GenericError: for invalid addresses
472 return IP4Address(address).family
473 except errors.IPAddressError:
477 return IP6Address(address).family
478 except errors.IPAddressError:
481 raise errors.IPAddressError("Invalid address '%s'" % address)
484 def GetVersionFromAddressFamily(family):
485 """Convert an IP address family to the corresponding IP version.
488 @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
489 @return: an int containing the IP version, one of L{constants.IP4_VERSION}
490 or L{constants.IP6_VERSION}
491 @raise errors.ProgrammerError: for unknown families
494 if family == socket.AF_INET:
495 return constants.IP4_VERSION
496 elif family == socket.AF_INET6:
497 return constants.IP6_VERSION
499 raise errors.ProgrammerError("%s is not a valid IP address family" % family)
502 def GetAddressFamilyFromVersion(version):
503 """Convert an IP version to the corresponding IP address family.
506 @param version: IP version, one of L{constants.IP4_VERSION} or
507 L{constants.IP6_VERSION}
508 @return: an int containing the IP address family, one of C{socket.AF_INET}
509 or C{socket.AF_INET6}
510 @raise errors.ProgrammerError: for unknown IP versions
513 if version == constants.IP4_VERSION:
514 return socket.AF_INET
515 elif version == constants.IP6_VERSION:
516 return socket.AF_INET6
518 raise errors.ProgrammerError("%s is not a valid IP version" % version)
521 def GetClassFromIpVersion(version):
522 """Return the IPAddress subclass for the given IP version.
525 @param version: IP version, one of L{constants.IP4_VERSION} or
526 L{constants.IP6_VERSION}
527 @return: a subclass of L{netutils.IPAddress}
528 @raise errors.ProgrammerError: for unknowo IP versions
531 if version == constants.IP4_VERSION:
533 elif version == constants.IP6_VERSION:
536 raise errors.ProgrammerError("%s is not a valid IP version" % version)
539 def GetClassFromIpFamily(family):
540 """Return the IPAddress subclass for the given IP family.
542 @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6}
543 @return: a subclass of L{netutils.IPAddress}
544 @raise errors.ProgrammerError: for unknowo IP versions
547 return IPAddress.GetClassFromIpVersion(
548 IPAddress.GetVersionFromAddressFamily(family))
551 def IsLoopback(cls, address):
552 """Determine whether it is a loopback address.
555 @param address: IP address to be checked
557 @return: True if loopback, False otherwise
561 return cls.InNetwork(cls.loopback_cidr, address)
562 except errors.IPAddressError:
566 class IP4Address(IPAddress):
567 """IPv4 address class.
571 family = socket.AF_INET
572 loopback_cidr = "127.0.0.0/8"
574 def __init__(self, address):
575 """Constructor for IPv4 address.
578 @param address: IP address
579 @raises errors.IPAddressError: if address invalid
582 IPAddress.__init__(self)
583 if not self.IsValid(address):
584 raise errors.IPAddressError("IPv4 Address %s invalid" % address)
586 self.address = address
589 def _GetIPIntFromString(address):
590 """Get integer value of IPv4 address.
593 @param address: IPv6 address
595 @return: integer value of given IP address
599 parts = address.split(".")
600 assert len(parts) == 4
602 address_int = (address_int << 8) | int(part)
607 class IP6Address(IPAddress):
608 """IPv6 address class.
612 family = socket.AF_INET6
613 loopback_cidr = "::1/128"
615 def __init__(self, address):
616 """Constructor for IPv6 address.
619 @param address: IP address
620 @raises errors.IPAddressError: if address invalid
623 IPAddress.__init__(self)
624 if not self.IsValid(address):
625 raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
626 self.address = address
629 def _GetIPIntFromString(address):
630 """Get integer value of IPv6 address.
633 @param address: IPv6 address
635 @return: integer value of given IP address
638 doublecolons = address.count("::")
639 assert not doublecolons > 1
640 if doublecolons == 1:
641 # We have a shorthand address, expand it
643 twoparts = address.split("::")
644 sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
645 parts = twoparts[0].split(":")
646 parts.extend(["0"] * (8 - sep))
647 parts += twoparts[1].split(":")
649 parts = address.split(":")
653 address_int = (address_int << 16) + int(part or "0", 16)
658 def FormatAddress(address, family=None):
659 """Format a socket address
661 @type address: family specific (usually tuple)
662 @param address: address, as reported by this class
663 @type family: integer
664 @param family: socket family (one of socket.AF_*) or None
669 family = IPAddress.GetAddressFamily(address[0])
670 except errors.IPAddressError:
671 raise errors.ParameterError(address)
673 if family == socket.AF_UNIX and len(address) == 3:
674 return "pid=%s, uid=%s, gid=%s" % address
676 if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
678 if family == socket.AF_INET6:
688 raise errors.ParameterError(family, address)