Statistics
| Branch: | Tag: | Revision:

root / lib / netutils.py @ 84a12e40

History | View | Annotate | Download (8.5 kB)

1 a744b676 Manuel Franceschini
#
2 a744b676 Manuel Franceschini
#
3 a744b676 Manuel Franceschini
4 a744b676 Manuel Franceschini
# Copyright (C) 2010 Google Inc.
5 a744b676 Manuel Franceschini
#
6 a744b676 Manuel Franceschini
# This program is free software; you can redistribute it and/or modify
7 a744b676 Manuel Franceschini
# it under the terms of the GNU General Public License as published by
8 a744b676 Manuel Franceschini
# the Free Software Foundation; either version 2 of the License, or
9 a744b676 Manuel Franceschini
# (at your option) any later version.
10 a744b676 Manuel Franceschini
#
11 a744b676 Manuel Franceschini
# This program is distributed in the hope that it will be useful, but
12 a744b676 Manuel Franceschini
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a744b676 Manuel Franceschini
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a744b676 Manuel Franceschini
# General Public License for more details.
15 a744b676 Manuel Franceschini
#
16 a744b676 Manuel Franceschini
# You should have received a copy of the GNU General Public License
17 a744b676 Manuel Franceschini
# along with this program; if not, write to the Free Software
18 a744b676 Manuel Franceschini
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a744b676 Manuel Franceschini
# 02110-1301, USA.
20 a744b676 Manuel Franceschini
21 a744b676 Manuel Franceschini
22 a744b676 Manuel Franceschini
"""Ganeti network utility module.
23 a744b676 Manuel Franceschini

24 a744b676 Manuel Franceschini
This module holds functions that can be used in both daemons (all) and
25 a744b676 Manuel Franceschini
the command line scripts.
26 a744b676 Manuel Franceschini

27 a744b676 Manuel Franceschini
"""
28 a744b676 Manuel Franceschini
29 a744b676 Manuel Franceschini
30 a744b676 Manuel Franceschini
import errno
31 a744b676 Manuel Franceschini
import re
32 a744b676 Manuel Franceschini
import socket
33 a744b676 Manuel Franceschini
import struct
34 a744b676 Manuel Franceschini
import IN
35 a744b676 Manuel Franceschini
36 a744b676 Manuel Franceschini
from ganeti import constants
37 a744b676 Manuel Franceschini
from ganeti import errors
38 a744b676 Manuel Franceschini
39 a744b676 Manuel Franceschini
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
40 a744b676 Manuel Franceschini
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
41 a744b676 Manuel Franceschini
#
42 a744b676 Manuel Franceschini
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
43 a744b676 Manuel Franceschini
# pid_t to "int".
44 a744b676 Manuel Franceschini
#
45 a744b676 Manuel Franceschini
# IEEE Std 1003.1-2008:
46 a744b676 Manuel Franceschini
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
47 a744b676 Manuel Franceschini
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
48 a744b676 Manuel Franceschini
_STRUCT_UCRED = "iII"
49 a744b676 Manuel Franceschini
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
50 a744b676 Manuel Franceschini
51 a744b676 Manuel Franceschini
52 a744b676 Manuel Franceschini
def GetSocketCredentials(sock):
53 a744b676 Manuel Franceschini
  """Returns the credentials of the foreign process connected to a socket.
54 a744b676 Manuel Franceschini

55 a744b676 Manuel Franceschini
  @param sock: Unix socket
56 a744b676 Manuel Franceschini
  @rtype: tuple; (number, number, number)
57 a744b676 Manuel Franceschini
  @return: The PID, UID and GID of the connected foreign process.
58 a744b676 Manuel Franceschini

59 a744b676 Manuel Franceschini
  """
60 a744b676 Manuel Franceschini
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
61 a744b676 Manuel Franceschini
                             _STRUCT_UCRED_SIZE)
62 a744b676 Manuel Franceschini
  return struct.unpack(_STRUCT_UCRED, peercred)
63 a744b676 Manuel Franceschini
64 a744b676 Manuel Franceschini
65 a744b676 Manuel Franceschini
def GetHostInfo(name=None):
66 a744b676 Manuel Franceschini
  """Lookup host name and raise an OpPrereqError for failures"""
67 a744b676 Manuel Franceschini
68 a744b676 Manuel Franceschini
  try:
69 a744b676 Manuel Franceschini
    return HostInfo(name)
70 a744b676 Manuel Franceschini
  except errors.ResolverError, err:
71 a744b676 Manuel Franceschini
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
72 a744b676 Manuel Franceschini
                               (err[0], err[2]), errors.ECODE_RESOLVER)
73 a744b676 Manuel Franceschini
74 a744b676 Manuel Franceschini
75 a744b676 Manuel Franceschini
class HostInfo:
76 a744b676 Manuel Franceschini
  """Class implementing resolver and hostname functionality
77 a744b676 Manuel Franceschini

78 a744b676 Manuel Franceschini
  """
79 a744b676 Manuel Franceschini
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
80 a744b676 Manuel Franceschini
81 a744b676 Manuel Franceschini
  def __init__(self, name=None):
82 a744b676 Manuel Franceschini
    """Initialize the host name object.
83 a744b676 Manuel Franceschini

84 a744b676 Manuel Franceschini
    If the name argument is not passed, it will use this system's
85 a744b676 Manuel Franceschini
    name.
86 a744b676 Manuel Franceschini

87 a744b676 Manuel Franceschini
    """
88 a744b676 Manuel Franceschini
    if name is None:
89 a744b676 Manuel Franceschini
      name = self.SysName()
90 a744b676 Manuel Franceschini
91 a744b676 Manuel Franceschini
    self.query = name
92 a744b676 Manuel Franceschini
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
93 a744b676 Manuel Franceschini
    self.ip = self.ipaddrs[0]
94 a744b676 Manuel Franceschini
95 a744b676 Manuel Franceschini
  def ShortName(self):
96 a744b676 Manuel Franceschini
    """Returns the hostname without domain.
97 a744b676 Manuel Franceschini

98 a744b676 Manuel Franceschini
    """
99 a744b676 Manuel Franceschini
    return self.name.split('.')[0]
100 a744b676 Manuel Franceschini
101 a744b676 Manuel Franceschini
  @staticmethod
102 a744b676 Manuel Franceschini
  def SysName():
103 a744b676 Manuel Franceschini
    """Return the current system's name.
104 a744b676 Manuel Franceschini

105 a744b676 Manuel Franceschini
    This is simply a wrapper over C{socket.gethostname()}.
106 a744b676 Manuel Franceschini

107 a744b676 Manuel Franceschini
    """
108 a744b676 Manuel Franceschini
    return socket.gethostname()
109 a744b676 Manuel Franceschini
110 a744b676 Manuel Franceschini
  @staticmethod
111 a744b676 Manuel Franceschini
  def LookupHostname(hostname):
112 a744b676 Manuel Franceschini
    """Look up hostname
113 a744b676 Manuel Franceschini

114 a744b676 Manuel Franceschini
    @type hostname: str
115 a744b676 Manuel Franceschini
    @param hostname: hostname to look up
116 a744b676 Manuel Franceschini

117 a744b676 Manuel Franceschini
    @rtype: tuple
118 a744b676 Manuel Franceschini
    @return: a tuple (name, aliases, ipaddrs) as returned by
119 a744b676 Manuel Franceschini
        C{socket.gethostbyname_ex}
120 a744b676 Manuel Franceschini
    @raise errors.ResolverError: in case of errors in resolving
121 a744b676 Manuel Franceschini

122 a744b676 Manuel Franceschini
    """
123 a744b676 Manuel Franceschini
    try:
124 a744b676 Manuel Franceschini
      result = socket.gethostbyname_ex(hostname)
125 a744b676 Manuel Franceschini
    except (socket.gaierror, socket.herror, socket.error), err:
126 a744b676 Manuel Franceschini
      # hostname not found in DNS, or other socket exception in the
127 a744b676 Manuel Franceschini
      # (code, description format)
128 a744b676 Manuel Franceschini
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
129 a744b676 Manuel Franceschini
130 a744b676 Manuel Franceschini
    return result
131 a744b676 Manuel Franceschini
132 a744b676 Manuel Franceschini
  @classmethod
133 a744b676 Manuel Franceschini
  def NormalizeName(cls, hostname):
134 a744b676 Manuel Franceschini
    """Validate and normalize the given hostname.
135 a744b676 Manuel Franceschini

136 a744b676 Manuel Franceschini
    @attention: the validation is a bit more relaxed than the standards
137 a744b676 Manuel Franceschini
        require; most importantly, we allow underscores in names
138 a744b676 Manuel Franceschini
    @raise errors.OpPrereqError: when the name is not valid
139 a744b676 Manuel Franceschini

140 a744b676 Manuel Franceschini
    """
141 a744b676 Manuel Franceschini
    hostname = hostname.lower()
142 a744b676 Manuel Franceschini
    if (not cls._VALID_NAME_RE.match(hostname) or
143 a744b676 Manuel Franceschini
        # double-dots, meaning empty label
144 a744b676 Manuel Franceschini
        ".." in hostname or
145 a744b676 Manuel Franceschini
        # empty initial label
146 a744b676 Manuel Franceschini
        hostname.startswith(".")):
147 a744b676 Manuel Franceschini
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
148 a744b676 Manuel Franceschini
                                 errors.ECODE_INVAL)
149 a744b676 Manuel Franceschini
    if hostname.endswith("."):
150 a744b676 Manuel Franceschini
      hostname = hostname.rstrip(".")
151 a744b676 Manuel Franceschini
    return hostname
152 a744b676 Manuel Franceschini
153 a744b676 Manuel Franceschini
154 a744b676 Manuel Franceschini
def _GenericIsValidIP(family, ip):
155 a744b676 Manuel Franceschini
  """Generic internal version of ip validation.
156 a744b676 Manuel Franceschini

157 a744b676 Manuel Franceschini
  @type family: int
158 a744b676 Manuel Franceschini
  @param family: socket.AF_INET | socket.AF_INET6
159 a744b676 Manuel Franceschini
  @type ip: str
160 a744b676 Manuel Franceschini
  @param ip: the address to be checked
161 a744b676 Manuel Franceschini
  @rtype: boolean
162 a744b676 Manuel Franceschini
  @return: True if ip is valid, False otherwise
163 a744b676 Manuel Franceschini

164 a744b676 Manuel Franceschini
  """
165 a744b676 Manuel Franceschini
  try:
166 a744b676 Manuel Franceschini
    socket.inet_pton(family, ip)
167 a744b676 Manuel Franceschini
    return True
168 a744b676 Manuel Franceschini
  except socket.error:
169 a744b676 Manuel Franceschini
    return False
170 a744b676 Manuel Franceschini
171 a744b676 Manuel Franceschini
172 a744b676 Manuel Franceschini
def IsValidIP4(ip):
173 a744b676 Manuel Franceschini
  """Verifies an IPv4 address.
174 a744b676 Manuel Franceschini

175 a744b676 Manuel Franceschini
  This function checks if the given address is a valid IPv4 address.
176 a744b676 Manuel Franceschini

177 a744b676 Manuel Franceschini
  @type ip: str
178 a744b676 Manuel Franceschini
  @param ip: the address to be checked
179 a744b676 Manuel Franceschini
  @rtype: boolean
180 a744b676 Manuel Franceschini
  @return: True if ip is valid, False otherwise
181 a744b676 Manuel Franceschini

182 a744b676 Manuel Franceschini
  """
183 a744b676 Manuel Franceschini
  return _GenericIsValidIP(socket.AF_INET, ip)
184 a744b676 Manuel Franceschini
185 a744b676 Manuel Franceschini
186 a744b676 Manuel Franceschini
def IsValidIP6(ip):
187 a744b676 Manuel Franceschini
  """Verifies an IPv6 address.
188 a744b676 Manuel Franceschini

189 a744b676 Manuel Franceschini
  This function checks if the given address is a valid IPv6 address.
190 a744b676 Manuel Franceschini

191 a744b676 Manuel Franceschini
  @type ip: str
192 a744b676 Manuel Franceschini
  @param ip: the address to be checked
193 a744b676 Manuel Franceschini
  @rtype: boolean
194 a744b676 Manuel Franceschini
  @return: True if ip is valid, False otherwise
195 a744b676 Manuel Franceschini

196 a744b676 Manuel Franceschini
  """
197 a744b676 Manuel Franceschini
  return _GenericIsValidIP(socket.AF_INET6, ip)
198 a744b676 Manuel Franceschini
199 a744b676 Manuel Franceschini
200 a744b676 Manuel Franceschini
def IsValidIP(ip):
201 a744b676 Manuel Franceschini
  """Verifies an IP address.
202 a744b676 Manuel Franceschini

203 a744b676 Manuel Franceschini
  This function checks if the given IP address (both IPv4 and IPv6) is valid.
204 a744b676 Manuel Franceschini

205 a744b676 Manuel Franceschini
  @type ip: str
206 a744b676 Manuel Franceschini
  @param ip: the address to be checked
207 a744b676 Manuel Franceschini
  @rtype: boolean
208 a744b676 Manuel Franceschini
  @return: True if ip is valid, False otherwise
209 a744b676 Manuel Franceschini

210 a744b676 Manuel Franceschini
  """
211 a744b676 Manuel Franceschini
  return IsValidIP4(ip) or IsValidIP6(ip)
212 a744b676 Manuel Franceschini
213 a744b676 Manuel Franceschini
214 a744b676 Manuel Franceschini
def GetAddressFamily(ip):
215 a744b676 Manuel Franceschini
  """Get the address family of the given address.
216 a744b676 Manuel Franceschini

217 a744b676 Manuel Franceschini
  @type ip: str
218 a744b676 Manuel Franceschini
  @param ip: ip address whose family will be returned
219 a744b676 Manuel Franceschini
  @rtype: int
220 a744b676 Manuel Franceschini
  @return: socket.AF_INET or socket.AF_INET6
221 a744b676 Manuel Franceschini
  @raise errors.GenericError: for invalid addresses
222 a744b676 Manuel Franceschini

223 a744b676 Manuel Franceschini
  """
224 a744b676 Manuel Franceschini
  if IsValidIP6(ip):
225 a744b676 Manuel Franceschini
    return socket.AF_INET6
226 a744b676 Manuel Franceschini
  elif IsValidIP4(ip):
227 a744b676 Manuel Franceschini
    return socket.AF_INET
228 a744b676 Manuel Franceschini
  else:
229 a744b676 Manuel Franceschini
    raise errors.GenericError("Address %s not valid" % ip)
230 a744b676 Manuel Franceschini
231 a744b676 Manuel Franceschini
232 a744b676 Manuel Franceschini
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
233 a744b676 Manuel Franceschini
  """Simple ping implementation using TCP connect(2).
234 a744b676 Manuel Franceschini

235 a744b676 Manuel Franceschini
  Check if the given IP is reachable by doing attempting a TCP connect
236 a744b676 Manuel Franceschini
  to it.
237 a744b676 Manuel Franceschini

238 a744b676 Manuel Franceschini
  @type target: str
239 a744b676 Manuel Franceschini
  @param target: the IP or hostname to ping
240 a744b676 Manuel Franceschini
  @type port: int
241 a744b676 Manuel Franceschini
  @param port: the port to connect to
242 a744b676 Manuel Franceschini
  @type timeout: int
243 a744b676 Manuel Franceschini
  @param timeout: the timeout on the connection attempt
244 a744b676 Manuel Franceschini
  @type live_port_needed: boolean
245 a744b676 Manuel Franceschini
  @param live_port_needed: whether a closed port will cause the
246 a744b676 Manuel Franceschini
      function to return failure, as if there was a timeout
247 a744b676 Manuel Franceschini
  @type source: str or None
248 a744b676 Manuel Franceschini
  @param source: if specified, will cause the connect to be made
249 a744b676 Manuel Franceschini
      from this specific source address; failures to bind other
250 a744b676 Manuel Franceschini
      than C{EADDRNOTAVAIL} will be ignored
251 a744b676 Manuel Franceschini

252 a744b676 Manuel Franceschini
  """
253 a744b676 Manuel Franceschini
  try:
254 a744b676 Manuel Franceschini
    family = GetAddressFamily(target)
255 a744b676 Manuel Franceschini
  except errors.GenericError:
256 a744b676 Manuel Franceschini
    return False
257 a744b676 Manuel Franceschini
258 a744b676 Manuel Franceschini
  sock = socket.socket(family, socket.SOCK_STREAM)
259 a744b676 Manuel Franceschini
  success = False
260 a744b676 Manuel Franceschini
261 a744b676 Manuel Franceschini
  if source is not None:
262 a744b676 Manuel Franceschini
    try:
263 a744b676 Manuel Franceschini
      sock.bind((source, 0))
264 a744b676 Manuel Franceschini
    except socket.error, (errcode, _):
265 a744b676 Manuel Franceschini
      if errcode == errno.EADDRNOTAVAIL:
266 a744b676 Manuel Franceschini
        success = False
267 a744b676 Manuel Franceschini
268 a744b676 Manuel Franceschini
  sock.settimeout(timeout)
269 a744b676 Manuel Franceschini
270 a744b676 Manuel Franceschini
  try:
271 a744b676 Manuel Franceschini
    sock.connect((target, port))
272 a744b676 Manuel Franceschini
    sock.close()
273 a744b676 Manuel Franceschini
    success = True
274 a744b676 Manuel Franceschini
  except socket.timeout:
275 a744b676 Manuel Franceschini
    success = False
276 a744b676 Manuel Franceschini
  except socket.error, (errcode, _):
277 a744b676 Manuel Franceschini
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
278 a744b676 Manuel Franceschini
279 a744b676 Manuel Franceschini
  return success
280 a744b676 Manuel Franceschini
281 a744b676 Manuel Franceschini
282 a744b676 Manuel Franceschini
def OwnIpAddress(address):
283 a744b676 Manuel Franceschini
  """Check if the current host has the the given IP address.
284 a744b676 Manuel Franceschini

285 a744b676 Manuel Franceschini
  This is done by trying to bind the given address. We return True if we
286 a744b676 Manuel Franceschini
  succeed or false if a socket.error is raised.
287 a744b676 Manuel Franceschini

288 a744b676 Manuel Franceschini
  @type address: string
289 a744b676 Manuel Franceschini
  @param address: the address to check
290 a744b676 Manuel Franceschini
  @rtype: bool
291 a744b676 Manuel Franceschini
  @return: True if we own the address
292 a744b676 Manuel Franceschini

293 a744b676 Manuel Franceschini
  """
294 a744b676 Manuel Franceschini
  family = GetAddressFamily(address)
295 a744b676 Manuel Franceschini
  s = socket.socket(family, socket.SOCK_DGRAM)
296 a744b676 Manuel Franceschini
  success = False
297 a744b676 Manuel Franceschini
  try:
298 a744b676 Manuel Franceschini
    try:
299 a744b676 Manuel Franceschini
      s.bind((address, 0))
300 a744b676 Manuel Franceschini
      success = True
301 a744b676 Manuel Franceschini
    except socket.error:
302 a744b676 Manuel Franceschini
      success = False
303 a744b676 Manuel Franceschini
  finally:
304 a744b676 Manuel Franceschini
    s.close()
305 a744b676 Manuel Franceschini
  return success
306 a744b676 Manuel Franceschini
307 a744b676 Manuel Franceschini
308 a744b676 Manuel Franceschini
def GetDaemonPort(daemon_name):
309 a744b676 Manuel Franceschini
  """Get the daemon port for this cluster.
310 a744b676 Manuel Franceschini

311 a744b676 Manuel Franceschini
  Note that this routine does not read a ganeti-specific file, but
312 a744b676 Manuel Franceschini
  instead uses C{socket.getservbyname} to allow pre-customization of
313 a744b676 Manuel Franceschini
  this parameter outside of Ganeti.
314 a744b676 Manuel Franceschini

315 a744b676 Manuel Franceschini
  @type daemon_name: string
316 a744b676 Manuel Franceschini
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
317 a744b676 Manuel Franceschini
  @rtype: int
318 a744b676 Manuel Franceschini

319 a744b676 Manuel Franceschini
  """
320 a744b676 Manuel Franceschini
  if daemon_name not in constants.DAEMONS_PORTS:
321 a744b676 Manuel Franceschini
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
322 a744b676 Manuel Franceschini
323 a744b676 Manuel Franceschini
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
324 a744b676 Manuel Franceschini
  try:
325 a744b676 Manuel Franceschini
    port = socket.getservbyname(daemon_name, proto)
326 a744b676 Manuel Franceschini
  except socket.error:
327 a744b676 Manuel Franceschini
    port = default_port
328 a744b676 Manuel Franceschini
329 a744b676 Manuel Franceschini
  return port