X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/17f7fd27541cff23ac871d2bac6b25757ada7437..b8203e1ee4e63032355b287e50527a5fdc91dbd5:/lib/netutils.py?ds=sidebyside diff --git a/lib/netutils.py b/lib/netutils.py index 9dbf97b..ac87d4d 100644 --- a/lib/netutils.py +++ b/lib/netutils.py @@ -28,13 +28,16 @@ the command line scripts. 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; }; @@ -48,6 +51,38 @@ from ganeti import errors _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"(?Pinet6?)\s+(?P%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. @@ -62,6 +97,39 @@ def GetSocketCredentials(sock): 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. @@ -187,7 +255,7 @@ def TcpPing(target, port, timeout=10, live_port_needed=False, source=None): 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 @@ -294,6 +362,20 @@ class IPAddress(object): 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. @@ -349,9 +431,9 @@ class IPAddress(object): 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 @@ -366,7 +448,7 @@ class IPAddress(object): @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 """ @@ -382,6 +464,73 @@ class IPAddress(object): 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. @@ -476,16 +625,16 @@ class IP6Address(IPAddress): # 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