import errno
+import os
import re
import socket
import struct
import IN
+import logging
from ganeti import constants
from ganeti import errors
+from ganeti import utils
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
_STRUCT_UCRED = "iII"
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
+# Regexes used to find IP addresses in the output of ip.
+_IP_RE_TEXT = r"[.:a-z0-9]+" # separate for testing purposes
+_IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT,
+ re.IGNORECASE)
+
+# Dict used to convert from a string representing an IP family to an IP
+# version
+_NAME_TO_IP_VER = {
+ "inet": constants.IP4_VERSION,
+ "inet6": constants.IP6_VERSION,
+ }
+
+
+def _GetIpAddressesFromIpOutput(ip_output):
+ """Parses the output of the ip command and retrieves the IP addresses and
+ version.
+
+ @param ip_output: string containing the output of the ip command;
+ @rtype: dict; (int, list)
+ @return: a dict having as keys the IP versions and as values the
+ corresponding list of addresses found in the IP output.
+
+ """
+ addr = dict((i, []) for i in _NAME_TO_IP_VER.values())
+
+ for row in ip_output.splitlines():
+ match = _IP_FAMILY_RE.search(row)
+ if match and IPAddress.IsValid(match.group("ip")):
+ addr[_NAME_TO_IP_VER[match.group("family")]].append(match.group("ip"))
+
+ return addr
+
def GetSocketCredentials(sock):
"""Returns the credentials of the foreign process connected to a socket.
return struct.unpack(_STRUCT_UCRED, peercred)
+def IsValidInterface(ifname):
+ """Validate an interface name.
+
+ @type ifname: string
+ @param ifname: Name of the network interface
+ @return: boolean indicating whether the interface name is valid or not.
+
+ """
+ return os.path.exists(utils.PathJoin("/sys/class/net", ifname))
+
+
+def GetInterfaceIpAddresses(ifname):
+ """Returns the IP addresses associated to the interface.
+
+ @type ifname: string
+ @param ifname: Name of the network interface
+ @return: A dict having for keys the IP version (either
+ L{constants.IP4_VERSION} or L{constants.IP6_VERSION}) and for
+ values the lists of IP addresses of the respective version
+ associated to the interface
+
+ """
+ result = utils.RunCmd([constants.IP_COMMAND_PATH, "-o", "addr", "show",
+ ifname])
+
+ if result.failed:
+ logging.error("Error running the ip command while getting the IP"
+ " addresses of %s", ifname)
+ return None
+
+ return _GetIpAddressesFromIpOutput(result.output)
+
+
def GetHostname(name=None, family=None):
"""Returns a Hostname object.
@param family: AF_INET | AF_INET6 | None
@rtype: L{Hostname}
@return: Hostname object
- @raise: errors.OpPrereqError
+ @raise errors.OpPrereqError: in case of errors in resolving
"""
try:
"""Class implementing resolver and hostname functionality.
"""
+ _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
+
def __init__(self, name=None, family=None):
"""Initialize the host name object.
@param name: hostname or None
"""
- if name is None:
- name = self.GetSysName()
-
- self.name = self.GetNormalizedName(name)
+ self.name = self.GetNormalizedName(self.GetFqdn(name))
self.ip = self.GetIP(self.name, family=family)
+ @classmethod
+ def GetSysName(cls):
+ """Legacy method the get the current system's name.
+
+ """
+ return cls.GetFqdn()
+
@staticmethod
- def GetSysName():
- """Return the current system's name.
+ def GetFqdn(hostname=None):
+ """Return fqdn.
- This is simply a wrapper over C{socket.gethostname()}.
+ If hostname is None the system's fqdn is returned.
+
+ @type hostname: str
+ @param hostname: name to be fqdn'ed
+ @rtype: str
+ @return: fqdn of given name, if it exists, unmodified name otherwise
"""
- return socket.gethostname()
+ if hostname is None:
+ return socket.getfqdn()
+ else:
+ return socket.getfqdn(hostname)
@staticmethod
def GetIP(hostname, family=None):
if family in (socket.AF_INET, socket.AF_INET6):
result = socket.getaddrinfo(hostname, None, family)
else:
- result = socket.getaddrinfo(hostname, None, socket.AF_INET)
+ result = socket.getaddrinfo(hostname, None)
except (socket.gaierror, socket.herror, socket.error), err:
# hostname not found in DNS, or other socket exception in the
# (code, description format)
# getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
# canonname, sockaddr). We return the first tuple's first address in
# sockaddr
- return result[0][4][0]
+ try:
+ return result[0][4][0]
+ except IndexError, err:
+ raise errors.ResolverError("Unknown error in getaddrinfo(): %s" % err)
- @staticmethod
- def GetNormalizedName(hostname):
+ @classmethod
+ def GetNormalizedName(cls, hostname):
"""Validate and normalize the given hostname.
@attention: the validation is a bit more relaxed than the standards
@raise errors.OpPrereqError: when the name is not valid
"""
- valid_name_re = re.compile("^[a-z0-9._-]{1,255}$")
hostname = hostname.lower()
- if (not valid_name_re.match(hostname) or
+ if (not cls._VALID_NAME_RE.match(hostname) or
# double-dots, meaning empty label
".." in hostname or
# empty initial label
to it.
@type target: str
- @param target: the IP or hostname to ping
+ @param target: the IP to ping
@type port: int
@param port: the port to connect to
@type timeout: int
return False
@classmethod
+ def ValidateNetmask(cls, netmask):
+ """Validate a netmask suffix in CIDR notation.
+
+ @type netmask: int
+ @param netmask: netmask suffix to validate
+ @rtype: bool
+ @return: True if valid, False otherwise
+
+ """
+ assert (isinstance(netmask, (int, long)))
+
+ return 0 < netmask <= cls.iplen
+
+ @classmethod
def Own(cls, address):
"""Check if the current host has the the given IP address.
assert 0 <= prefix <= cls.iplen
target_int = cls._GetIPIntFromString(subnet[0])
# Convert prefix netmask to integer value of netmask
- netmask_int = (2**cls.iplen)-1 ^ ((2**cls.iplen)-1 >> prefix)
+ netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
# Calculate hostmask
- hostmask_int = netmask_int ^ (2**cls.iplen)-1
+ hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
# Calculate network address by and'ing netmask
network_int = target_int & netmask_int
# Calculate broadcast address by or'ing hostmask
@type address: str
@param address: ip address whose family will be returned
@rtype: int
- @return: socket.AF_INET or socket.AF_INET6
+ @return: C{socket.AF_INET} or C{socket.AF_INET6}
@raise errors.GenericError: for invalid addresses
"""
raise errors.IPAddressError("Invalid address '%s'" % address)
+ @staticmethod
+ def GetVersionFromAddressFamily(family):
+ """Convert an IP address family to the corresponding IP version.
+
+ @type family: int
+ @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
+ @return: an int containing the IP version, one of L{constants.IP4_VERSION}
+ or L{constants.IP6_VERSION}
+ @raise errors.ProgrammerError: for unknown families
+
+ """
+ if family == socket.AF_INET:
+ return constants.IP4_VERSION
+ elif family == socket.AF_INET6:
+ return constants.IP6_VERSION
+
+ raise errors.ProgrammerError("%s is not a valid IP address family" % family)
+
+ @staticmethod
+ def GetAddressFamilyFromVersion(version):
+ """Convert an IP version to the corresponding IP address family.
+
+ @type version: int
+ @param version: IP version, one of L{constants.IP4_VERSION} or
+ L{constants.IP6_VERSION}
+ @return: an int containing the IP address family, one of C{socket.AF_INET}
+ or C{socket.AF_INET6}
+ @raise errors.ProgrammerError: for unknown IP versions
+
+ """
+ if version == constants.IP4_VERSION:
+ return socket.AF_INET
+ elif version == constants.IP6_VERSION:
+ return socket.AF_INET6
+
+ raise errors.ProgrammerError("%s is not a valid IP version" % version)
+
+ @staticmethod
+ def GetClassFromIpVersion(version):
+ """Return the IPAddress subclass for the given IP version.
+
+ @type version: int
+ @param version: IP version, one of L{constants.IP4_VERSION} or
+ L{constants.IP6_VERSION}
+ @return: a subclass of L{netutils.IPAddress}
+ @raise errors.ProgrammerError: for unknowo IP versions
+
+ """
+ if version == constants.IP4_VERSION:
+ return IP4Address
+ elif version == constants.IP6_VERSION:
+ return IP6Address
+
+ raise errors.ProgrammerError("%s is not a valid IP version" % version)
+
+ @staticmethod
+ def GetClassFromIpFamily(family):
+ """Return the IPAddress subclass for the given IP family.
+
+ @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6}
+ @return: a subclass of L{netutils.IPAddress}
+ @raise errors.ProgrammerError: for unknowo IP versions
+
+ """
+ return IPAddress.GetClassFromIpVersion(
+ IPAddress.GetVersionFromAddressFamily(family))
+
@classmethod
def IsLoopback(cls, address):
"""Determine whether it is a loopback address.
"""Get integer value of IPv4 address.
@type address: str
- @param: IPv6 address
+ @param address: IPv6 address
@rtype: int
@return: integer value of given IP address
"""Get integer value of IPv6 address.
@type address: str
- @param: IPv6 address
+ @param address: IPv6 address
@rtype: int
@return: integer value of given IP address
# We have a shorthand address, expand it
parts = []
twoparts = address.split("::")
- sep = len(twoparts[0].split(':')) + len(twoparts[1].split(':'))
- parts = twoparts[0].split(':')
- [parts.append("0") for _ in range(8 - sep)]
- parts += twoparts[1].split(':')
+ sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
+ parts = twoparts[0].split(":")
+ parts.extend(["0"] * (8 - sep))
+ parts += twoparts[1].split(":")
else:
parts = address.split(":")
address_int = 0
for part in parts:
- address_int = (address_int << 16) + int(part or '0', 16)
+ address_int = (address_int << 16) + int(part or "0", 16)
return address_int
-def FormatAddress(family, address):
+
+def FormatAddress(address, family=None):
"""Format a socket address
- @type family: integer
- @param family: socket family (one of socket.AF_*)
@type address: family specific (usually tuple)
@param address: address, as reported by this class
+ @type family: integer
+ @param family: socket family (one of socket.AF_*) or None
"""
+ if family is None:
+ try:
+ family = IPAddress.GetAddressFamily(address[0])
+ except errors.IPAddressError:
+ raise errors.ParameterError(address)
+
if family == socket.AF_UNIX and len(address) == 3:
return "pid=%s, uid=%s, gid=%s" % address