Merge branch 'devel-2.4'
[ganeti-local] / lib / netutils.py
index f5f5bb1..fc864eb 100644 (file)
@@ -62,75 +62,104 @@ def GetSocketCredentials(sock):
   return struct.unpack(_STRUCT_UCRED, peercred)
 
 
   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:
   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)
 
 
   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}$")
 
 
   """
   _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.
 
     """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
 
   @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
 
   @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
 
     @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:
     @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])
 
     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
 
   @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
     """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
   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
   @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
     """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
 
     @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
     """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
 
     @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
       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)