X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/8b312c1d7f1f7154e4f3c1ba9fffa1da678832f9..2492231f7381ce33f2164322f799c84b2d7cceef:/lib/netutils.py?ds=sidebyside diff --git a/lib/netutils.py b/lib/netutils.py index f5f5bb1..fc864eb 100644 --- a/lib/netutils.py +++ b/lib/netutils.py @@ -62,75 +62,104 @@ def GetSocketCredentials(sock): return struct.unpack(_STRUCT_UCRED, peercred) -def GetHostInfo(name=None): - """Lookup host name and raise an OpPrereqError for failures""" +def GetHostname(name=None, family=None): + """Returns a Hostname object. + @type name: str + @param name: hostname or None + @type family: int + @param family: AF_INET | AF_INET6 | None + @rtype: L{Hostname} + @return: Hostname object + @raise errors.OpPrereqError: in case of errors in resolving + + """ try: - return HostInfo(name) + return Hostname(name=name, family=family) except errors.ResolverError, err: raise errors.OpPrereqError("The given name (%s) does not resolve: %s" % (err[0], err[2]), errors.ECODE_RESOLVER) -class HostInfo: - """Class implementing resolver and hostname functionality +class Hostname: + """Class implementing resolver and hostname functionality. """ _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$") - def __init__(self, name=None): + def __init__(self, name=None, family=None): """Initialize the host name object. - If the name argument is not passed, it will use this system's - name. + If the name argument is None, it will use this system's name. - """ - if name is None: - name = self.SysName() + @type family: int + @param family: AF_INET | AF_INET6 | None + @type name: str + @param name: hostname or None - self.query = name - self.name, self.aliases, self.ipaddrs = self.LookupHostname(name) - self.ip = self.ipaddrs[0] + """ + self.name = self.GetNormalizedName(self.GetFqdn(name)) + self.ip = self.GetIP(self.name, family=family) - def ShortName(self): - """Returns the hostname without domain. + @classmethod + def GetSysName(cls): + """Legacy method the get the current system's name. """ - return self.name.split('.')[0] + return cls.GetFqdn() @staticmethod - def SysName(): - """Return the current system's name. + def GetFqdn(hostname=None): + """Return fqdn. + + If hostname is None the system's fqdn is returned. - This is simply a wrapper over C{socket.gethostname()}. + @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 LookupHostname(hostname): - """Look up hostname + def GetIP(hostname, family=None): + """Return IP address of given hostname. + + Supports both IPv4 and IPv6. @type hostname: str @param hostname: hostname to look up - - @rtype: tuple - @return: a tuple (name, aliases, ipaddrs) as returned by - C{socket.gethostbyname_ex} + @type family: int + @param family: AF_INET | AF_INET6 | None + @rtype: str + @return: IP address @raise errors.ResolverError: in case of errors in resolving """ try: - result = socket.gethostbyname_ex(hostname) + if family in (socket.AF_INET, socket.AF_INET6): + result = socket.getaddrinfo(hostname, None, family) + else: + 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) raise errors.ResolverError(hostname, err.args[0], err.args[1]) - return result + # getaddrinfo() returns a list of 5-tupes (family, socktype, proto, + # canonname, sockaddr). We return the first tuple's first address in + # sockaddr + try: + return result[0][4][0] + except IndexError, err: + raise errors.ResolverError("Unknown error in getaddrinfo(): %s" % err) @classmethod - def NormalizeName(cls, hostname): + def GetNormalizedName(cls, hostname): """Validate and normalize the given hostname. @attention: the validation is a bit more relaxed than the standards @@ -158,7 +187,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 @@ -396,7 +425,7 @@ class IP4Address(IPAddress): """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 @@ -436,7 +465,7 @@ class IP6Address(IPAddress): """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 @@ -459,3 +488,36 @@ class IP6Address(IPAddress): address_int = (address_int << 16) + int(part or '0', 16) return address_int + + +def FormatAddress(address, family=None): + """Format a socket address + + @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 + + if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2: + host, port = address + if family == socket.AF_INET6: + res = "[%s]" % host + else: + res = host + + if port is not None: + res += ":%s" % port + + return res + + raise errors.ParameterError(family, address)