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.GetNormalizedName(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(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 return socket.getfqdn()
199 return socket.getfqdn(hostname)
202 def GetIP(hostname, family=None):
203 """Return IP address of given hostname.
205 Supports both IPv4 and IPv6.
208 @param hostname: hostname to look up
210 @param family: AF_INET | AF_INET6 | None
213 @raise errors.ResolverError: in case of errors in resolving
217 if family in (socket.AF_INET, socket.AF_INET6):
218 result = socket.getaddrinfo(hostname, None, family)
220 result = socket.getaddrinfo(hostname, None)
221 except (socket.gaierror, socket.herror, socket.error), err:
222 # hostname not found in DNS, or other socket exception in the
223 # (code, description format)
224 raise errors.ResolverError(hostname, err.args[0], err.args[1])
226 # getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
227 # canonname, sockaddr). We return the first tuple's first address in
230 return result[0][4][0]
231 except IndexError, err:
232 # we don't have here an actual error code, it's just that the
233 # data type returned by getaddrinfo is not what we expected;
234 # let's keep the same format in the exception arguments with a
236 raise errors.ResolverError(hostname, 0,
237 "Unknown error in getaddrinfo(): %s" % err)
240 def GetNormalizedName(cls, hostname):
241 """Validate and normalize the given hostname.
243 @attention: the validation is a bit more relaxed than the standards
244 require; most importantly, we allow underscores in names
245 @raise errors.OpPrereqError: when the name is not valid
248 hostname = hostname.lower()
249 if (not cls._VALID_NAME_RE.match(hostname) or
250 # double-dots, meaning empty label
252 # empty initial label
253 hostname.startswith(".")):
254 raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
256 if hostname.endswith("."):
257 hostname = hostname.rstrip(".")
261 def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
262 """Simple ping implementation using TCP connect(2).
264 Check if the given IP is reachable by doing attempting a TCP connect
268 @param target: the IP to ping
270 @param port: the port to connect to
272 @param timeout: the timeout on the connection attempt
273 @type live_port_needed: boolean
274 @param live_port_needed: whether a closed port will cause the
275 function to return failure, as if there was a timeout
276 @type source: str or None
277 @param source: if specified, will cause the connect to be made
278 from this specific source address; failures to bind other
279 than C{EADDRNOTAVAIL} will be ignored
283 family = IPAddress.GetAddressFamily(target)
284 except errors.GenericError:
287 sock = socket.socket(family, socket.SOCK_STREAM)
290 if source is not None:
292 sock.bind((source, 0))
293 except socket.error, err:
294 if err[0] == errno.EADDRNOTAVAIL:
297 sock.settimeout(timeout)
300 sock.connect((target, port))
303 except socket.timeout:
305 except socket.error, err:
306 success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED)
311 def GetDaemonPort(daemon_name):
312 """Get the daemon port for this cluster.
314 Note that this routine does not read a ganeti-specific file, but
315 instead uses C{socket.getservbyname} to allow pre-customization of
316 this parameter outside of Ganeti.
318 @type daemon_name: string
319 @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
323 if daemon_name not in constants.DAEMONS_PORTS:
324 raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
326 (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
328 port = socket.getservbyname(daemon_name, proto)
335 class IPAddress(object):
336 """Class that represents an IP address.
344 def _GetIPIntFromString(address):
345 """Abstract method to please pylint.
348 raise NotImplementedError
351 def IsValid(cls, address):
352 """Validate a IP address.
355 @param address: IP address to be checked
357 @return: True if valid, False otherwise
360 if cls.family is None:
362 family = cls.GetAddressFamily(address)
363 except errors.IPAddressError:
369 socket.inet_pton(family, address)
375 def ValidateNetmask(cls, netmask):
376 """Validate a netmask suffix in CIDR notation.
379 @param netmask: netmask suffix to validate
381 @return: True if valid, False otherwise
384 assert (isinstance(netmask, (int, long)))
386 return 0 < netmask <= cls.iplen
389 def Own(cls, address):
390 """Check if the current host has the the given IP address.
392 This is done by trying to bind the given address. We return True if we
393 succeed or false if a socket.error is raised.
396 @param address: IP address to be checked
398 @return: True if we own the address, False otherwise
401 if cls.family is None:
403 family = cls.GetAddressFamily(address)
404 except errors.IPAddressError:
409 s = socket.socket(family, socket.SOCK_DGRAM)
422 def InNetwork(cls, cidr, address):
423 """Determine whether an address is within a network.
426 @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
428 @param address: IP address
430 @return: True if address is in cidr, False otherwise
433 address_int = cls._GetIPIntFromString(address)
434 subnet = cidr.split("/")
435 assert len(subnet) == 2
437 prefix = int(subnet[1])
441 assert 0 <= prefix <= cls.iplen
442 target_int = cls._GetIPIntFromString(subnet[0])
443 # Convert prefix netmask to integer value of netmask
444 netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
446 hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
447 # Calculate network address by and'ing netmask
448 network_int = target_int & netmask_int
449 # Calculate broadcast address by or'ing hostmask
450 broadcast_int = target_int | hostmask_int
452 return network_int <= address_int <= broadcast_int
455 def GetAddressFamily(address):
456 """Get the address family of the given address.
459 @param address: ip address whose family will be returned
461 @return: C{socket.AF_INET} or C{socket.AF_INET6}
462 @raise errors.GenericError: for invalid addresses
466 return IP4Address(address).family
467 except errors.IPAddressError:
471 return IP6Address(address).family
472 except errors.IPAddressError:
475 raise errors.IPAddressError("Invalid address '%s'" % address)
478 def GetVersionFromAddressFamily(family):
479 """Convert an IP address family to the corresponding IP version.
482 @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
483 @return: an int containing the IP version, one of L{constants.IP4_VERSION}
484 or L{constants.IP6_VERSION}
485 @raise errors.ProgrammerError: for unknown families
488 if family == socket.AF_INET:
489 return constants.IP4_VERSION
490 elif family == socket.AF_INET6:
491 return constants.IP6_VERSION
493 raise errors.ProgrammerError("%s is not a valid IP address family" % family)
496 def GetAddressFamilyFromVersion(version):
497 """Convert an IP version to the corresponding IP address family.
500 @param version: IP version, one of L{constants.IP4_VERSION} or
501 L{constants.IP6_VERSION}
502 @return: an int containing the IP address family, one of C{socket.AF_INET}
503 or C{socket.AF_INET6}
504 @raise errors.ProgrammerError: for unknown IP versions
507 if version == constants.IP4_VERSION:
508 return socket.AF_INET
509 elif version == constants.IP6_VERSION:
510 return socket.AF_INET6
512 raise errors.ProgrammerError("%s is not a valid IP version" % version)
515 def GetClassFromIpVersion(version):
516 """Return the IPAddress subclass for the given IP version.
519 @param version: IP version, one of L{constants.IP4_VERSION} or
520 L{constants.IP6_VERSION}
521 @return: a subclass of L{netutils.IPAddress}
522 @raise errors.ProgrammerError: for unknowo IP versions
525 if version == constants.IP4_VERSION:
527 elif version == constants.IP6_VERSION:
530 raise errors.ProgrammerError("%s is not a valid IP version" % version)
533 def GetClassFromIpFamily(family):
534 """Return the IPAddress subclass for the given IP family.
536 @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6}
537 @return: a subclass of L{netutils.IPAddress}
538 @raise errors.ProgrammerError: for unknowo IP versions
541 return IPAddress.GetClassFromIpVersion(
542 IPAddress.GetVersionFromAddressFamily(family))
545 def IsLoopback(cls, address):
546 """Determine whether it is a loopback address.
549 @param address: IP address to be checked
551 @return: True if loopback, False otherwise
555 return cls.InNetwork(cls.loopback_cidr, address)
556 except errors.IPAddressError:
560 class IP4Address(IPAddress):
561 """IPv4 address class.
565 family = socket.AF_INET
566 loopback_cidr = "127.0.0.0/8"
568 def __init__(self, address):
569 """Constructor for IPv4 address.
572 @param address: IP address
573 @raises errors.IPAddressError: if address invalid
576 IPAddress.__init__(self)
577 if not self.IsValid(address):
578 raise errors.IPAddressError("IPv4 Address %s invalid" % address)
580 self.address = address
583 def _GetIPIntFromString(address):
584 """Get integer value of IPv4 address.
587 @param address: IPv6 address
589 @return: integer value of given IP address
593 parts = address.split(".")
594 assert len(parts) == 4
596 address_int = (address_int << 8) | int(part)
601 class IP6Address(IPAddress):
602 """IPv6 address class.
606 family = socket.AF_INET6
607 loopback_cidr = "::1/128"
609 def __init__(self, address):
610 """Constructor for IPv6 address.
613 @param address: IP address
614 @raises errors.IPAddressError: if address invalid
617 IPAddress.__init__(self)
618 if not self.IsValid(address):
619 raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
620 self.address = address
623 def _GetIPIntFromString(address):
624 """Get integer value of IPv6 address.
627 @param address: IPv6 address
629 @return: integer value of given IP address
632 doublecolons = address.count("::")
633 assert not doublecolons > 1
634 if doublecolons == 1:
635 # We have a shorthand address, expand it
637 twoparts = address.split("::")
638 sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
639 parts = twoparts[0].split(":")
640 parts.extend(["0"] * (8 - sep))
641 parts += twoparts[1].split(":")
643 parts = address.split(":")
647 address_int = (address_int << 16) + int(part or "0", 16)
652 def FormatAddress(address, family=None):
653 """Format a socket address
655 @type address: family specific (usually tuple)
656 @param address: address, as reported by this class
657 @type family: integer
658 @param family: socket family (one of socket.AF_*) or None
663 family = IPAddress.GetAddressFamily(address[0])
664 except errors.IPAddressError:
665 raise errors.ParameterError(address)
667 if family == socket.AF_UNIX and len(address) == 3:
668 return "pid=%s, uid=%s, gid=%s" % address
670 if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
672 if family == socket.AF_INET6:
682 raise errors.ParameterError(family, address)