Revision b705c7a6 lib/netutils.py

b/lib/netutils.py
62 62
  return struct.unpack(_STRUCT_UCRED, peercred)
63 63

  
64 64

  
65
def GetHostInfo(name=None):
66
  """Lookup host name and raise an OpPrereqError for failures"""
65
def GetHostname(name=None, family=None):
66
  """Returns a Hostname object.
67 67

  
68
  @type name: str
69
  @param name: hostname or None
70
  @type family: int
71
  @param family: AF_INET | AF_INET6 | None
72
  @rtype: L{Hostname}
73
  @return: Hostname object
74
  @raise: errors.OpPrereqError
75

  
76
  """
68 77
  try:
69
    return HostInfo(name)
78
    return Hostname(name=name, family=family)
70 79
  except errors.ResolverError, err:
71 80
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
72 81
                               (err[0], err[2]), errors.ECODE_RESOLVER)
73 82

  
74 83

  
75
class HostInfo:
76
  """Class implementing resolver and hostname functionality
84
class Hostname:
85
  """Class implementing resolver and hostname functionality.
77 86

  
78 87
  """
79
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
80

  
81
  def __init__(self, name=None):
88
  def __init__(self, name=None, family=None):
82 89
    """Initialize the host name object.
83 90

  
84
    If the name argument is not passed, it will use this system's
85
    name.
91
    If the name argument is None, it will use this system's name.
92

  
93
    @type family: int
94
    @param family: AF_INET | AF_INET6 | None
95
    @type name: str
96
    @param name: hostname or None
86 97

  
87 98
    """
88 99
    if name is None:
89
      name = self.SysName()
90

  
91
    self.query = name
92
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
93
    self.ip = self.ipaddrs[0]
100
      name = self.GetSysName()
94 101

  
95
  def ShortName(self):
96
    """Returns the hostname without domain.
97

  
98
    """
99
    return self.name.split('.')[0]
102
    self.name = self.GetNormalizedName(name)
103
    self.ip = self.GetIP(self.name, family=family)
100 104

  
101 105
  @staticmethod
102
  def SysName():
106
  def GetSysName():
103 107
    """Return the current system's name.
104 108

  
105 109
    This is simply a wrapper over C{socket.gethostname()}.
......
108 112
    return socket.gethostname()
109 113

  
110 114
  @staticmethod
111
  def LookupHostname(hostname):
112
    """Look up hostname
115
  def GetIP(hostname, family=None):
116
    """Return IP address of given hostname.
117

  
118
    Supports both IPv4 and IPv6.
113 119

  
114 120
    @type hostname: str
115 121
    @param hostname: hostname to look up
116

  
117
    @rtype: tuple
118
    @return: a tuple (name, aliases, ipaddrs) as returned by
119
        C{socket.gethostbyname_ex}
122
    @type family: int
123
    @param family: AF_INET | AF_INET6 | None
124
    @rtype: str
125
    @return: IP address
120 126
    @raise errors.ResolverError: in case of errors in resolving
121 127

  
122 128
    """
123 129
    try:
124
      result = socket.gethostbyname_ex(hostname)
130
      if family in (socket.AF_INET, socket.AF_INET6):
131
        result = socket.getaddrinfo(hostname, None, family)
132
      else:
133
        result = socket.getaddrinfo(hostname, None, socket.AF_INET)
125 134
    except (socket.gaierror, socket.herror, socket.error), err:
126 135
      # hostname not found in DNS, or other socket exception in the
127 136
      # (code, description format)
128 137
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
129 138

  
130
    return result
139
    # getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
140
    # canonname, sockaddr). We return the first tuple's first address in
141
    # sockaddr
142
    return result[0][4][0]
131 143

  
132
  @classmethod
133
  def NormalizeName(cls, hostname):
144
  @staticmethod
145
  def GetNormalizedName(hostname):
134 146
    """Validate and normalize the given hostname.
135 147

  
136 148
    @attention: the validation is a bit more relaxed than the standards
......
138 150
    @raise errors.OpPrereqError: when the name is not valid
139 151

  
140 152
    """
153
    valid_name_re = re.compile("^[a-z0-9._-]{1,255}$")
141 154
    hostname = hostname.lower()
142
    if (not cls._VALID_NAME_RE.match(hostname) or
155
    if (not valid_name_re.match(hostname) or
143 156
        # double-dots, meaning empty label
144 157
        ".." in hostname or
145 158
        # empty initial label

Also available in: Unified diff