Statistics
| Branch: | Tag: | Revision:

root / lib / netutils.py @ 33c730a2

History | View | Annotate | Download (18.9 kB)

1 a744b676 Manuel Franceschini
#
2 a744b676 Manuel Franceschini
#
3 a744b676 Manuel Franceschini
4 8ad0da1e Iustin Pop
# Copyright (C) 2010, 2011, 2012 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 37531236 Andrea Spadaccini
import os
32 a744b676 Manuel Franceschini
import re
33 a744b676 Manuel Franceschini
import socket
34 a744b676 Manuel Franceschini
import struct
35 a744b676 Manuel Franceschini
import IN
36 37531236 Andrea Spadaccini
import logging
37 a744b676 Manuel Franceschini
38 a744b676 Manuel Franceschini
from ganeti import constants
39 a744b676 Manuel Franceschini
from ganeti import errors
40 37531236 Andrea Spadaccini
from ganeti import utils
41 e91b297c Michael Hanselmann
from ganeti import vcluster
42 a744b676 Manuel Franceschini
43 a744b676 Manuel Franceschini
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
44 a744b676 Manuel Franceschini
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
45 a744b676 Manuel Franceschini
#
46 a744b676 Manuel Franceschini
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
47 a744b676 Manuel Franceschini
# pid_t to "int".
48 a744b676 Manuel Franceschini
#
49 a744b676 Manuel Franceschini
# IEEE Std 1003.1-2008:
50 a744b676 Manuel Franceschini
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
51 a744b676 Manuel Franceschini
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
52 a744b676 Manuel Franceschini
_STRUCT_UCRED = "iII"
53 a744b676 Manuel Franceschini
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
54 a744b676 Manuel Franceschini
55 37531236 Andrea Spadaccini
# Regexes used to find IP addresses in the output of ip.
56 37531236 Andrea Spadaccini
_IP_RE_TEXT = r"[.:a-z0-9]+"      # separate for testing purposes
57 37531236 Andrea Spadaccini
_IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT,
58 37531236 Andrea Spadaccini
                           re.IGNORECASE)
59 37531236 Andrea Spadaccini
60 37531236 Andrea Spadaccini
# Dict used to convert from a string representing an IP family to an IP
61 37531236 Andrea Spadaccini
# version
62 e687ec01 Michael Hanselmann
_NAME_TO_IP_VER = {
63 37531236 Andrea Spadaccini
  "inet": constants.IP4_VERSION,
64 37531236 Andrea Spadaccini
  "inet6": constants.IP6_VERSION,
65 37531236 Andrea Spadaccini
  }
66 37531236 Andrea Spadaccini
67 37531236 Andrea Spadaccini
68 37531236 Andrea Spadaccini
def _GetIpAddressesFromIpOutput(ip_output):
69 37531236 Andrea Spadaccini
  """Parses the output of the ip command and retrieves the IP addresses and
70 37531236 Andrea Spadaccini
  version.
71 37531236 Andrea Spadaccini

72 37531236 Andrea Spadaccini
  @param ip_output: string containing the output of the ip command;
73 37531236 Andrea Spadaccini
  @rtype: dict; (int, list)
74 37531236 Andrea Spadaccini
  @return: a dict having as keys the IP versions and as values the
75 37531236 Andrea Spadaccini
           corresponding list of addresses found in the IP output.
76 37531236 Andrea Spadaccini

77 37531236 Andrea Spadaccini
  """
78 37531236 Andrea Spadaccini
  addr = dict((i, []) for i in _NAME_TO_IP_VER.values())
79 37531236 Andrea Spadaccini
80 37531236 Andrea Spadaccini
  for row in ip_output.splitlines():
81 37531236 Andrea Spadaccini
    match = _IP_FAMILY_RE.search(row)
82 37531236 Andrea Spadaccini
    if match and IPAddress.IsValid(match.group("ip")):
83 37531236 Andrea Spadaccini
      addr[_NAME_TO_IP_VER[match.group("family")]].append(match.group("ip"))
84 37531236 Andrea Spadaccini
85 37531236 Andrea Spadaccini
  return addr
86 37531236 Andrea Spadaccini
87 a744b676 Manuel Franceschini
88 a744b676 Manuel Franceschini
def GetSocketCredentials(sock):
89 a744b676 Manuel Franceschini
  """Returns the credentials of the foreign process connected to a socket.
90 a744b676 Manuel Franceschini

91 a744b676 Manuel Franceschini
  @param sock: Unix socket
92 a744b676 Manuel Franceschini
  @rtype: tuple; (number, number, number)
93 a744b676 Manuel Franceschini
  @return: The PID, UID and GID of the connected foreign process.
94 a744b676 Manuel Franceschini

95 a744b676 Manuel Franceschini
  """
96 a744b676 Manuel Franceschini
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
97 a744b676 Manuel Franceschini
                             _STRUCT_UCRED_SIZE)
98 a744b676 Manuel Franceschini
  return struct.unpack(_STRUCT_UCRED, peercred)
99 a744b676 Manuel Franceschini
100 a744b676 Manuel Franceschini
101 37531236 Andrea Spadaccini
def IsValidInterface(ifname):
102 37531236 Andrea Spadaccini
  """Validate an interface name.
103 37531236 Andrea Spadaccini

104 37531236 Andrea Spadaccini
  @type ifname: string
105 37531236 Andrea Spadaccini
  @param ifname: Name of the network interface
106 37531236 Andrea Spadaccini
  @return: boolean indicating whether the interface name is valid or not.
107 37531236 Andrea Spadaccini

108 37531236 Andrea Spadaccini
  """
109 37531236 Andrea Spadaccini
  return os.path.exists(utils.PathJoin("/sys/class/net", ifname))
110 37531236 Andrea Spadaccini
111 37531236 Andrea Spadaccini
112 37531236 Andrea Spadaccini
def GetInterfaceIpAddresses(ifname):
113 37531236 Andrea Spadaccini
  """Returns the IP addresses associated to the interface.
114 37531236 Andrea Spadaccini

115 37531236 Andrea Spadaccini
  @type ifname: string
116 37531236 Andrea Spadaccini
  @param ifname: Name of the network interface
117 37531236 Andrea Spadaccini
  @return: A dict having for keys the IP version (either
118 37531236 Andrea Spadaccini
           L{constants.IP4_VERSION} or L{constants.IP6_VERSION}) and for
119 37531236 Andrea Spadaccini
           values the lists of IP addresses of the respective version
120 37531236 Andrea Spadaccini
           associated to the interface
121 37531236 Andrea Spadaccini

122 37531236 Andrea Spadaccini
  """
123 37531236 Andrea Spadaccini
  result = utils.RunCmd([constants.IP_COMMAND_PATH, "-o", "addr", "show",
124 37531236 Andrea Spadaccini
                         ifname])
125 37531236 Andrea Spadaccini
126 37531236 Andrea Spadaccini
  if result.failed:
127 37531236 Andrea Spadaccini
    logging.error("Error running the ip command while getting the IP"
128 37531236 Andrea Spadaccini
                  " addresses of %s", ifname)
129 37531236 Andrea Spadaccini
    return None
130 37531236 Andrea Spadaccini
131 37531236 Andrea Spadaccini
  return _GetIpAddressesFromIpOutput(result.output)
132 37531236 Andrea Spadaccini
133 37531236 Andrea Spadaccini
134 b705c7a6 Manuel Franceschini
def GetHostname(name=None, family=None):
135 b705c7a6 Manuel Franceschini
  """Returns a Hostname object.
136 a744b676 Manuel Franceschini

137 b705c7a6 Manuel Franceschini
  @type name: str
138 b705c7a6 Manuel Franceschini
  @param name: hostname or None
139 b705c7a6 Manuel Franceschini
  @type family: int
140 b705c7a6 Manuel Franceschini
  @param family: AF_INET | AF_INET6 | None
141 b705c7a6 Manuel Franceschini
  @rtype: L{Hostname}
142 b705c7a6 Manuel Franceschini
  @return: Hostname object
143 17f7fd27 Manuel Franceschini
  @raise errors.OpPrereqError: in case of errors in resolving
144 b705c7a6 Manuel Franceschini

145 b705c7a6 Manuel Franceschini
  """
146 a744b676 Manuel Franceschini
  try:
147 b705c7a6 Manuel Franceschini
    return Hostname(name=name, family=family)
148 a744b676 Manuel Franceschini
  except errors.ResolverError, err:
149 a744b676 Manuel Franceschini
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
150 a744b676 Manuel Franceschini
                               (err[0], err[2]), errors.ECODE_RESOLVER)
151 a744b676 Manuel Franceschini
152 a744b676 Manuel Franceschini
153 b705c7a6 Manuel Franceschini
class Hostname:
154 b705c7a6 Manuel Franceschini
  """Class implementing resolver and hostname functionality.
155 a744b676 Manuel Franceschini

156 a744b676 Manuel Franceschini
  """
157 e7b3ad26 Manuel Franceschini
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
158 e7b3ad26 Manuel Franceschini
159 b705c7a6 Manuel Franceschini
  def __init__(self, name=None, family=None):
160 a744b676 Manuel Franceschini
    """Initialize the host name object.
161 a744b676 Manuel Franceschini

162 b705c7a6 Manuel Franceschini
    If the name argument is None, it will use this system's name.
163 b705c7a6 Manuel Franceschini

164 b705c7a6 Manuel Franceschini
    @type family: int
165 b705c7a6 Manuel Franceschini
    @param family: AF_INET | AF_INET6 | None
166 b705c7a6 Manuel Franceschini
    @type name: str
167 b705c7a6 Manuel Franceschini
    @param name: hostname or None
168 a744b676 Manuel Franceschini

169 a744b676 Manuel Franceschini
    """
170 5f30ea3f Michael Hanselmann
    self.name = self.GetFqdn(name)
171 b705c7a6 Manuel Franceschini
    self.ip = self.GetIP(self.name, family=family)
172 a744b676 Manuel Franceschini
173 f3044516 Manuel Franceschini
  @classmethod
174 f3044516 Manuel Franceschini
  def GetSysName(cls):
175 f3044516 Manuel Franceschini
    """Legacy method the get the current system's name.
176 f3044516 Manuel Franceschini

177 f3044516 Manuel Franceschini
    """
178 f3044516 Manuel Franceschini
    return cls.GetFqdn()
179 f3044516 Manuel Franceschini
180 5f30ea3f Michael Hanselmann
  @classmethod
181 5f30ea3f Michael Hanselmann
  def GetFqdn(cls, hostname=None):
182 f3044516 Manuel Franceschini
    """Return fqdn.
183 f3044516 Manuel Franceschini

184 f3044516 Manuel Franceschini
    If hostname is None the system's fqdn is returned.
185 a744b676 Manuel Franceschini

186 f3044516 Manuel Franceschini
    @type hostname: str
187 f3044516 Manuel Franceschini
    @param hostname: name to be fqdn'ed
188 f3044516 Manuel Franceschini
    @rtype: str
189 f3044516 Manuel Franceschini
    @return: fqdn of given name, if it exists, unmodified name otherwise
190 a744b676 Manuel Franceschini

191 a744b676 Manuel Franceschini
    """
192 f3044516 Manuel Franceschini
    if hostname is None:
193 e91b297c Michael Hanselmann
      virtfqdn = vcluster.GetVirtualHostname()
194 e91b297c Michael Hanselmann
      if virtfqdn:
195 5f30ea3f Michael Hanselmann
        result = virtfqdn
196 e91b297c Michael Hanselmann
      else:
197 5f30ea3f Michael Hanselmann
        result = socket.getfqdn()
198 f3044516 Manuel Franceschini
    else:
199 5f30ea3f Michael Hanselmann
      result = socket.getfqdn(hostname)
200 5f30ea3f Michael Hanselmann
201 5f30ea3f Michael Hanselmann
    return cls.GetNormalizedName(result)
202 a744b676 Manuel Franceschini
203 a744b676 Manuel Franceschini
  @staticmethod
204 b705c7a6 Manuel Franceschini
  def GetIP(hostname, family=None):
205 b705c7a6 Manuel Franceschini
    """Return IP address of given hostname.
206 b705c7a6 Manuel Franceschini

207 b705c7a6 Manuel Franceschini
    Supports both IPv4 and IPv6.
208 a744b676 Manuel Franceschini

209 a744b676 Manuel Franceschini
    @type hostname: str
210 a744b676 Manuel Franceschini
    @param hostname: hostname to look up
211 b705c7a6 Manuel Franceschini
    @type family: int
212 b705c7a6 Manuel Franceschini
    @param family: AF_INET | AF_INET6 | None
213 b705c7a6 Manuel Franceschini
    @rtype: str
214 b705c7a6 Manuel Franceschini
    @return: IP address
215 a744b676 Manuel Franceschini
    @raise errors.ResolverError: in case of errors in resolving
216 a744b676 Manuel Franceschini

217 a744b676 Manuel Franceschini
    """
218 a744b676 Manuel Franceschini
    try:
219 b705c7a6 Manuel Franceschini
      if family in (socket.AF_INET, socket.AF_INET6):
220 b705c7a6 Manuel Franceschini
        result = socket.getaddrinfo(hostname, None, family)
221 b705c7a6 Manuel Franceschini
      else:
222 b43dcc5a Manuel Franceschini
        result = socket.getaddrinfo(hostname, None)
223 a744b676 Manuel Franceschini
    except (socket.gaierror, socket.herror, socket.error), err:
224 a744b676 Manuel Franceschini
      # hostname not found in DNS, or other socket exception in the
225 a744b676 Manuel Franceschini
      # (code, description format)
226 a744b676 Manuel Franceschini
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
227 a744b676 Manuel Franceschini
228 b705c7a6 Manuel Franceschini
    # getaddrinfo() returns a list of 5-tupes (family, socktype, proto,
229 b705c7a6 Manuel Franceschini
    # canonname, sockaddr). We return the first tuple's first address in
230 b705c7a6 Manuel Franceschini
    # sockaddr
231 e7b3ad26 Manuel Franceschini
    try:
232 e7b3ad26 Manuel Franceschini
      return result[0][4][0]
233 e7b3ad26 Manuel Franceschini
    except IndexError, err:
234 39d1744a Iustin Pop
      # we don't have here an actual error code, it's just that the
235 39d1744a Iustin Pop
      # data type returned by getaddrinfo is not what we expected;
236 39d1744a Iustin Pop
      # let's keep the same format in the exception arguments with a
237 39d1744a Iustin Pop
      # dummy error code
238 39d1744a Iustin Pop
      raise errors.ResolverError(hostname, 0,
239 39d1744a Iustin Pop
                                 "Unknown error in getaddrinfo(): %s" % err)
240 a744b676 Manuel Franceschini
241 e7b3ad26 Manuel Franceschini
  @classmethod
242 e7b3ad26 Manuel Franceschini
  def GetNormalizedName(cls, hostname):
243 a744b676 Manuel Franceschini
    """Validate and normalize the given hostname.
244 a744b676 Manuel Franceschini

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

249 a744b676 Manuel Franceschini
    """
250 a744b676 Manuel Franceschini
    hostname = hostname.lower()
251 e7b3ad26 Manuel Franceschini
    if (not cls._VALID_NAME_RE.match(hostname) or
252 a744b676 Manuel Franceschini
        # double-dots, meaning empty label
253 a744b676 Manuel Franceschini
        ".." in hostname or
254 a744b676 Manuel Franceschini
        # empty initial label
255 a744b676 Manuel Franceschini
        hostname.startswith(".")):
256 a744b676 Manuel Franceschini
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
257 a744b676 Manuel Franceschini
                                 errors.ECODE_INVAL)
258 a744b676 Manuel Franceschini
    if hostname.endswith("."):
259 a744b676 Manuel Franceschini
      hostname = hostname.rstrip(".")
260 a744b676 Manuel Franceschini
    return hostname
261 a744b676 Manuel Franceschini
262 a744b676 Manuel Franceschini
263 a744b676 Manuel Franceschini
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
264 a744b676 Manuel Franceschini
  """Simple ping implementation using TCP connect(2).
265 a744b676 Manuel Franceschini

266 a744b676 Manuel Franceschini
  Check if the given IP is reachable by doing attempting a TCP connect
267 a744b676 Manuel Franceschini
  to it.
268 a744b676 Manuel Franceschini

269 a744b676 Manuel Franceschini
  @type target: str
270 77b02a99 Renรฉ Nussbaumer
  @param target: the IP to ping
271 a744b676 Manuel Franceschini
  @type port: int
272 a744b676 Manuel Franceschini
  @param port: the port to connect to
273 a744b676 Manuel Franceschini
  @type timeout: int
274 a744b676 Manuel Franceschini
  @param timeout: the timeout on the connection attempt
275 a744b676 Manuel Franceschini
  @type live_port_needed: boolean
276 a744b676 Manuel Franceschini
  @param live_port_needed: whether a closed port will cause the
277 a744b676 Manuel Franceschini
      function to return failure, as if there was a timeout
278 a744b676 Manuel Franceschini
  @type source: str or None
279 a744b676 Manuel Franceschini
  @param source: if specified, will cause the connect to be made
280 a744b676 Manuel Franceschini
      from this specific source address; failures to bind other
281 a744b676 Manuel Franceschini
      than C{EADDRNOTAVAIL} will be ignored
282 a744b676 Manuel Franceschini

283 a744b676 Manuel Franceschini
  """
284 25ba209e Michael Hanselmann
  logging.debug("Attempting to reach TCP port %s on target %s with a timeout"
285 25ba209e Michael Hanselmann
                " of %s seconds", port, target, timeout)
286 25ba209e Michael Hanselmann
287 a744b676 Manuel Franceschini
  try:
288 8b312c1d Manuel Franceschini
    family = IPAddress.GetAddressFamily(target)
289 25ba209e Michael Hanselmann
  except errors.IPAddressError, err:
290 25ba209e Michael Hanselmann
    raise errors.ProgrammerError("Family of IP address given in parameter"
291 25ba209e Michael Hanselmann
                                 " 'target' can't be determined: %s" % err)
292 a744b676 Manuel Franceschini
293 a744b676 Manuel Franceschini
  sock = socket.socket(family, socket.SOCK_STREAM)
294 a744b676 Manuel Franceschini
  success = False
295 a744b676 Manuel Franceschini
296 a744b676 Manuel Franceschini
  if source is not None:
297 a744b676 Manuel Franceschini
    try:
298 a744b676 Manuel Franceschini
      sock.bind((source, 0))
299 8ad0da1e Iustin Pop
    except socket.error, err:
300 8ad0da1e Iustin Pop
      if err[0] == errno.EADDRNOTAVAIL:
301 a744b676 Manuel Franceschini
        success = False
302 a744b676 Manuel Franceschini
303 a744b676 Manuel Franceschini
  sock.settimeout(timeout)
304 a744b676 Manuel Franceschini
305 a744b676 Manuel Franceschini
  try:
306 a744b676 Manuel Franceschini
    sock.connect((target, port))
307 a744b676 Manuel Franceschini
    sock.close()
308 a744b676 Manuel Franceschini
    success = True
309 a744b676 Manuel Franceschini
  except socket.timeout:
310 a744b676 Manuel Franceschini
    success = False
311 8ad0da1e Iustin Pop
  except socket.error, err:
312 8ad0da1e Iustin Pop
    success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED)
313 a744b676 Manuel Franceschini
314 a744b676 Manuel Franceschini
  return success
315 a744b676 Manuel Franceschini
316 a744b676 Manuel Franceschini
317 a744b676 Manuel Franceschini
def GetDaemonPort(daemon_name):
318 a744b676 Manuel Franceschini
  """Get the daemon port for this cluster.
319 a744b676 Manuel Franceschini

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

324 a744b676 Manuel Franceschini
  @type daemon_name: string
325 a744b676 Manuel Franceschini
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
326 a744b676 Manuel Franceschini
  @rtype: int
327 a744b676 Manuel Franceschini

328 a744b676 Manuel Franceschini
  """
329 a744b676 Manuel Franceschini
  if daemon_name not in constants.DAEMONS_PORTS:
330 a744b676 Manuel Franceschini
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
331 a744b676 Manuel Franceschini
332 a744b676 Manuel Franceschini
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
333 a744b676 Manuel Franceschini
  try:
334 a744b676 Manuel Franceschini
    port = socket.getservbyname(daemon_name, proto)
335 a744b676 Manuel Franceschini
  except socket.error:
336 a744b676 Manuel Franceschini
    port = default_port
337 a744b676 Manuel Franceschini
338 a744b676 Manuel Franceschini
  return port
339 8b312c1d Manuel Franceschini
340 8b312c1d Manuel Franceschini
341 8b312c1d Manuel Franceschini
class IPAddress(object):
342 8b312c1d Manuel Franceschini
  """Class that represents an IP address.
343 8b312c1d Manuel Franceschini

344 8b312c1d Manuel Franceschini
  """
345 8b312c1d Manuel Franceschini
  iplen = 0
346 8b312c1d Manuel Franceschini
  family = None
347 8b312c1d Manuel Franceschini
  loopback_cidr = None
348 8b312c1d Manuel Franceschini
349 8b312c1d Manuel Franceschini
  @staticmethod
350 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
351 8b312c1d Manuel Franceschini
    """Abstract method to please pylint.
352 8b312c1d Manuel Franceschini

353 8b312c1d Manuel Franceschini
    """
354 8b312c1d Manuel Franceschini
    raise NotImplementedError
355 8b312c1d Manuel Franceschini
356 8b312c1d Manuel Franceschini
  @classmethod
357 8b312c1d Manuel Franceschini
  def IsValid(cls, address):
358 8b312c1d Manuel Franceschini
    """Validate a IP address.
359 8b312c1d Manuel Franceschini

360 8b312c1d Manuel Franceschini
    @type address: str
361 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
362 8b312c1d Manuel Franceschini
    @rtype: bool
363 8b312c1d Manuel Franceschini
    @return: True if valid, False otherwise
364 8b312c1d Manuel Franceschini

365 8b312c1d Manuel Franceschini
    """
366 8b312c1d Manuel Franceschini
    if cls.family is None:
367 8b312c1d Manuel Franceschini
      try:
368 8b312c1d Manuel Franceschini
        family = cls.GetAddressFamily(address)
369 8b312c1d Manuel Franceschini
      except errors.IPAddressError:
370 8b312c1d Manuel Franceschini
        return False
371 8b312c1d Manuel Franceschini
    else:
372 8b312c1d Manuel Franceschini
      family = cls.family
373 8b312c1d Manuel Franceschini
374 8b312c1d Manuel Franceschini
    try:
375 8b312c1d Manuel Franceschini
      socket.inet_pton(family, address)
376 8b312c1d Manuel Franceschini
      return True
377 8b312c1d Manuel Franceschini
    except socket.error:
378 8b312c1d Manuel Franceschini
      return False
379 8b312c1d Manuel Franceschini
380 8b312c1d Manuel Franceschini
  @classmethod
381 7df2c4f0 Andrea Spadaccini
  def ValidateNetmask(cls, netmask):
382 7df2c4f0 Andrea Spadaccini
    """Validate a netmask suffix in CIDR notation.
383 7df2c4f0 Andrea Spadaccini

384 7df2c4f0 Andrea Spadaccini
    @type netmask: int
385 7df2c4f0 Andrea Spadaccini
    @param netmask: netmask suffix to validate
386 7df2c4f0 Andrea Spadaccini
    @rtype: bool
387 7df2c4f0 Andrea Spadaccini
    @return: True if valid, False otherwise
388 7df2c4f0 Andrea Spadaccini

389 7df2c4f0 Andrea Spadaccini
    """
390 7df2c4f0 Andrea Spadaccini
    assert (isinstance(netmask, (int, long)))
391 7df2c4f0 Andrea Spadaccini
392 7df2c4f0 Andrea Spadaccini
    return 0 < netmask <= cls.iplen
393 7df2c4f0 Andrea Spadaccini
394 7df2c4f0 Andrea Spadaccini
  @classmethod
395 8b312c1d Manuel Franceschini
  def Own(cls, address):
396 8b312c1d Manuel Franceschini
    """Check if the current host has the the given IP address.
397 8b312c1d Manuel Franceschini

398 8b312c1d Manuel Franceschini
    This is done by trying to bind the given address. We return True if we
399 8b312c1d Manuel Franceschini
    succeed or false if a socket.error is raised.
400 8b312c1d Manuel Franceschini

401 8b312c1d Manuel Franceschini
    @type address: str
402 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
403 8b312c1d Manuel Franceschini
    @rtype: bool
404 8b312c1d Manuel Franceschini
    @return: True if we own the address, False otherwise
405 8b312c1d Manuel Franceschini

406 8b312c1d Manuel Franceschini
    """
407 8b312c1d Manuel Franceschini
    if cls.family is None:
408 8b312c1d Manuel Franceschini
      try:
409 8b312c1d Manuel Franceschini
        family = cls.GetAddressFamily(address)
410 8b312c1d Manuel Franceschini
      except errors.IPAddressError:
411 8b312c1d Manuel Franceschini
        return False
412 8b312c1d Manuel Franceschini
    else:
413 8b312c1d Manuel Franceschini
      family = cls.family
414 8b312c1d Manuel Franceschini
415 8b312c1d Manuel Franceschini
    s = socket.socket(family, socket.SOCK_DGRAM)
416 8b312c1d Manuel Franceschini
    success = False
417 8b312c1d Manuel Franceschini
    try:
418 8b312c1d Manuel Franceschini
      try:
419 8b312c1d Manuel Franceschini
        s.bind((address, 0))
420 8b312c1d Manuel Franceschini
        success = True
421 8b312c1d Manuel Franceschini
      except socket.error:
422 8b312c1d Manuel Franceschini
        success = False
423 8b312c1d Manuel Franceschini
    finally:
424 8b312c1d Manuel Franceschini
      s.close()
425 8b312c1d Manuel Franceschini
    return success
426 8b312c1d Manuel Franceschini
427 8b312c1d Manuel Franceschini
  @classmethod
428 8b312c1d Manuel Franceschini
  def InNetwork(cls, cidr, address):
429 8b312c1d Manuel Franceschini
    """Determine whether an address is within a network.
430 8b312c1d Manuel Franceschini

431 8b312c1d Manuel Franceschini
    @type cidr: string
432 8b312c1d Manuel Franceschini
    @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64'
433 8b312c1d Manuel Franceschini
    @type address: str
434 8b312c1d Manuel Franceschini
    @param address: IP address
435 8b312c1d Manuel Franceschini
    @rtype: bool
436 8b312c1d Manuel Franceschini
    @return: True if address is in cidr, False otherwise
437 8b312c1d Manuel Franceschini

438 8b312c1d Manuel Franceschini
    """
439 8b312c1d Manuel Franceschini
    address_int = cls._GetIPIntFromString(address)
440 8b312c1d Manuel Franceschini
    subnet = cidr.split("/")
441 8b312c1d Manuel Franceschini
    assert len(subnet) == 2
442 8b312c1d Manuel Franceschini
    try:
443 8b312c1d Manuel Franceschini
      prefix = int(subnet[1])
444 8b312c1d Manuel Franceschini
    except ValueError:
445 8b312c1d Manuel Franceschini
      return False
446 8b312c1d Manuel Franceschini
447 8b312c1d Manuel Franceschini
    assert 0 <= prefix <= cls.iplen
448 8b312c1d Manuel Franceschini
    target_int = cls._GetIPIntFromString(subnet[0])
449 8b312c1d Manuel Franceschini
    # Convert prefix netmask to integer value of netmask
450 e687ec01 Michael Hanselmann
    netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix)
451 8b312c1d Manuel Franceschini
    # Calculate hostmask
452 e687ec01 Michael Hanselmann
    hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1
453 8b312c1d Manuel Franceschini
    # Calculate network address by and'ing netmask
454 8b312c1d Manuel Franceschini
    network_int = target_int & netmask_int
455 8b312c1d Manuel Franceschini
    # Calculate broadcast address by or'ing hostmask
456 8b312c1d Manuel Franceschini
    broadcast_int = target_int | hostmask_int
457 8b312c1d Manuel Franceschini
458 8b312c1d Manuel Franceschini
    return network_int <= address_int <= broadcast_int
459 8b312c1d Manuel Franceschini
460 8b312c1d Manuel Franceschini
  @staticmethod
461 8b312c1d Manuel Franceschini
  def GetAddressFamily(address):
462 8b312c1d Manuel Franceschini
    """Get the address family of the given address.
463 8b312c1d Manuel Franceschini

464 8b312c1d Manuel Franceschini
    @type address: str
465 8b312c1d Manuel Franceschini
    @param address: ip address whose family will be returned
466 8b312c1d Manuel Franceschini
    @rtype: int
467 37531236 Andrea Spadaccini
    @return: C{socket.AF_INET} or C{socket.AF_INET6}
468 8b312c1d Manuel Franceschini
    @raise errors.GenericError: for invalid addresses
469 8b312c1d Manuel Franceschini

470 8b312c1d Manuel Franceschini
    """
471 8b312c1d Manuel Franceschini
    try:
472 8b312c1d Manuel Franceschini
      return IP4Address(address).family
473 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
474 8b312c1d Manuel Franceschini
      pass
475 8b312c1d Manuel Franceschini
476 8b312c1d Manuel Franceschini
    try:
477 8b312c1d Manuel Franceschini
      return IP6Address(address).family
478 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
479 8b312c1d Manuel Franceschini
      pass
480 8b312c1d Manuel Franceschini
481 8b312c1d Manuel Franceschini
    raise errors.IPAddressError("Invalid address '%s'" % address)
482 8b312c1d Manuel Franceschini
483 37531236 Andrea Spadaccini
  @staticmethod
484 37531236 Andrea Spadaccini
  def GetVersionFromAddressFamily(family):
485 37531236 Andrea Spadaccini
    """Convert an IP address family to the corresponding IP version.
486 37531236 Andrea Spadaccini

487 37531236 Andrea Spadaccini
    @type family: int
488 37531236 Andrea Spadaccini
    @param family: IP address family, one of socket.AF_INET or socket.AF_INET6
489 37531236 Andrea Spadaccini
    @return: an int containing the IP version, one of L{constants.IP4_VERSION}
490 37531236 Andrea Spadaccini
             or L{constants.IP6_VERSION}
491 37531236 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknown families
492 37531236 Andrea Spadaccini

493 37531236 Andrea Spadaccini
    """
494 37531236 Andrea Spadaccini
    if family == socket.AF_INET:
495 37531236 Andrea Spadaccini
      return constants.IP4_VERSION
496 37531236 Andrea Spadaccini
    elif family == socket.AF_INET6:
497 37531236 Andrea Spadaccini
      return constants.IP6_VERSION
498 37531236 Andrea Spadaccini
499 37531236 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP address family" % family)
500 37531236 Andrea Spadaccini
501 37531236 Andrea Spadaccini
  @staticmethod
502 37531236 Andrea Spadaccini
  def GetAddressFamilyFromVersion(version):
503 37531236 Andrea Spadaccini
    """Convert an IP version to the corresponding IP address family.
504 37531236 Andrea Spadaccini

505 37531236 Andrea Spadaccini
    @type version: int
506 37531236 Andrea Spadaccini
    @param version: IP version, one of L{constants.IP4_VERSION} or
507 37531236 Andrea Spadaccini
                    L{constants.IP6_VERSION}
508 37531236 Andrea Spadaccini
    @return: an int containing the IP address family, one of C{socket.AF_INET}
509 37531236 Andrea Spadaccini
             or C{socket.AF_INET6}
510 37531236 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknown IP versions
511 37531236 Andrea Spadaccini

512 37531236 Andrea Spadaccini
    """
513 37531236 Andrea Spadaccini
    if version == constants.IP4_VERSION:
514 37531236 Andrea Spadaccini
      return socket.AF_INET
515 37531236 Andrea Spadaccini
    elif version == constants.IP6_VERSION:
516 37531236 Andrea Spadaccini
      return socket.AF_INET6
517 37531236 Andrea Spadaccini
518 37531236 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP version" % version)
519 37531236 Andrea Spadaccini
520 7df2c4f0 Andrea Spadaccini
  @staticmethod
521 7df2c4f0 Andrea Spadaccini
  def GetClassFromIpVersion(version):
522 7df2c4f0 Andrea Spadaccini
    """Return the IPAddress subclass for the given IP version.
523 7df2c4f0 Andrea Spadaccini

524 7df2c4f0 Andrea Spadaccini
    @type version: int
525 7df2c4f0 Andrea Spadaccini
    @param version: IP version, one of L{constants.IP4_VERSION} or
526 7df2c4f0 Andrea Spadaccini
                    L{constants.IP6_VERSION}
527 7df2c4f0 Andrea Spadaccini
    @return: a subclass of L{netutils.IPAddress}
528 7df2c4f0 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknowo IP versions
529 7df2c4f0 Andrea Spadaccini

530 7df2c4f0 Andrea Spadaccini
    """
531 7df2c4f0 Andrea Spadaccini
    if version == constants.IP4_VERSION:
532 7df2c4f0 Andrea Spadaccini
      return IP4Address
533 7df2c4f0 Andrea Spadaccini
    elif version == constants.IP6_VERSION:
534 7df2c4f0 Andrea Spadaccini
      return IP6Address
535 7df2c4f0 Andrea Spadaccini
536 7df2c4f0 Andrea Spadaccini
    raise errors.ProgrammerError("%s is not a valid IP version" % version)
537 7df2c4f0 Andrea Spadaccini
538 7df2c4f0 Andrea Spadaccini
  @staticmethod
539 7df2c4f0 Andrea Spadaccini
  def GetClassFromIpFamily(family):
540 7df2c4f0 Andrea Spadaccini
    """Return the IPAddress subclass for the given IP family.
541 7df2c4f0 Andrea Spadaccini

542 7df2c4f0 Andrea Spadaccini
    @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6}
543 7df2c4f0 Andrea Spadaccini
    @return: a subclass of L{netutils.IPAddress}
544 7df2c4f0 Andrea Spadaccini
    @raise errors.ProgrammerError: for unknowo IP versions
545 7df2c4f0 Andrea Spadaccini

546 7df2c4f0 Andrea Spadaccini
    """
547 7df2c4f0 Andrea Spadaccini
    return IPAddress.GetClassFromIpVersion(
548 7df2c4f0 Andrea Spadaccini
              IPAddress.GetVersionFromAddressFamily(family))
549 7df2c4f0 Andrea Spadaccini
550 8b312c1d Manuel Franceschini
  @classmethod
551 8b312c1d Manuel Franceschini
  def IsLoopback(cls, address):
552 8b312c1d Manuel Franceschini
    """Determine whether it is a loopback address.
553 8b312c1d Manuel Franceschini

554 8b312c1d Manuel Franceschini
    @type address: str
555 8b312c1d Manuel Franceschini
    @param address: IP address to be checked
556 8b312c1d Manuel Franceschini
    @rtype: bool
557 8b312c1d Manuel Franceschini
    @return: True if loopback, False otherwise
558 8b312c1d Manuel Franceschini

559 8b312c1d Manuel Franceschini
    """
560 8b312c1d Manuel Franceschini
    try:
561 8b312c1d Manuel Franceschini
      return cls.InNetwork(cls.loopback_cidr, address)
562 8b312c1d Manuel Franceschini
    except errors.IPAddressError:
563 8b312c1d Manuel Franceschini
      return False
564 8b312c1d Manuel Franceschini
565 8b312c1d Manuel Franceschini
566 8b312c1d Manuel Franceschini
class IP4Address(IPAddress):
567 8b312c1d Manuel Franceschini
  """IPv4 address class.
568 8b312c1d Manuel Franceschini

569 8b312c1d Manuel Franceschini
  """
570 8b312c1d Manuel Franceschini
  iplen = 32
571 8b312c1d Manuel Franceschini
  family = socket.AF_INET
572 8b312c1d Manuel Franceschini
  loopback_cidr = "127.0.0.0/8"
573 8b312c1d Manuel Franceschini
574 8b312c1d Manuel Franceschini
  def __init__(self, address):
575 8b312c1d Manuel Franceschini
    """Constructor for IPv4 address.
576 8b312c1d Manuel Franceschini

577 8b312c1d Manuel Franceschini
    @type address: str
578 8b312c1d Manuel Franceschini
    @param address: IP address
579 8b312c1d Manuel Franceschini
    @raises errors.IPAddressError: if address invalid
580 8b312c1d Manuel Franceschini

581 8b312c1d Manuel Franceschini
    """
582 8b312c1d Manuel Franceschini
    IPAddress.__init__(self)
583 8b312c1d Manuel Franceschini
    if not self.IsValid(address):
584 8b312c1d Manuel Franceschini
      raise errors.IPAddressError("IPv4 Address %s invalid" % address)
585 8b312c1d Manuel Franceschini
586 8b312c1d Manuel Franceschini
    self.address = address
587 8b312c1d Manuel Franceschini
588 8b312c1d Manuel Franceschini
  @staticmethod
589 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
590 8b312c1d Manuel Franceschini
    """Get integer value of IPv4 address.
591 8b312c1d Manuel Franceschini

592 8b312c1d Manuel Franceschini
    @type address: str
593 17f7fd27 Manuel Franceschini
    @param address: IPv6 address
594 8b312c1d Manuel Franceschini
    @rtype: int
595 8b312c1d Manuel Franceschini
    @return: integer value of given IP address
596 8b312c1d Manuel Franceschini

597 8b312c1d Manuel Franceschini
    """
598 8b312c1d Manuel Franceschini
    address_int = 0
599 8b312c1d Manuel Franceschini
    parts = address.split(".")
600 8b312c1d Manuel Franceschini
    assert len(parts) == 4
601 8b312c1d Manuel Franceschini
    for part in parts:
602 8b312c1d Manuel Franceschini
      address_int = (address_int << 8) | int(part)
603 8b312c1d Manuel Franceschini
604 8b312c1d Manuel Franceschini
    return address_int
605 8b312c1d Manuel Franceschini
606 8b312c1d Manuel Franceschini
607 8b312c1d Manuel Franceschini
class IP6Address(IPAddress):
608 8b312c1d Manuel Franceschini
  """IPv6 address class.
609 8b312c1d Manuel Franceschini

610 8b312c1d Manuel Franceschini
  """
611 8b312c1d Manuel Franceschini
  iplen = 128
612 8b312c1d Manuel Franceschini
  family = socket.AF_INET6
613 8b312c1d Manuel Franceschini
  loopback_cidr = "::1/128"
614 8b312c1d Manuel Franceschini
615 8b312c1d Manuel Franceschini
  def __init__(self, address):
616 8b312c1d Manuel Franceschini
    """Constructor for IPv6 address.
617 8b312c1d Manuel Franceschini

618 8b312c1d Manuel Franceschini
    @type address: str
619 8b312c1d Manuel Franceschini
    @param address: IP address
620 8b312c1d Manuel Franceschini
    @raises errors.IPAddressError: if address invalid
621 8b312c1d Manuel Franceschini

622 8b312c1d Manuel Franceschini
    """
623 8b312c1d Manuel Franceschini
    IPAddress.__init__(self)
624 8b312c1d Manuel Franceschini
    if not self.IsValid(address):
625 8b312c1d Manuel Franceschini
      raise errors.IPAddressError("IPv6 Address [%s] invalid" % address)
626 8b312c1d Manuel Franceschini
    self.address = address
627 8b312c1d Manuel Franceschini
628 8b312c1d Manuel Franceschini
  @staticmethod
629 8b312c1d Manuel Franceschini
  def _GetIPIntFromString(address):
630 8b312c1d Manuel Franceschini
    """Get integer value of IPv6 address.
631 8b312c1d Manuel Franceschini

632 8b312c1d Manuel Franceschini
    @type address: str
633 17f7fd27 Manuel Franceschini
    @param address: IPv6 address
634 8b312c1d Manuel Franceschini
    @rtype: int
635 8b312c1d Manuel Franceschini
    @return: integer value of given IP address
636 8b312c1d Manuel Franceschini

637 8b312c1d Manuel Franceschini
    """
638 8b312c1d Manuel Franceschini
    doublecolons = address.count("::")
639 8b312c1d Manuel Franceschini
    assert not doublecolons > 1
640 8b312c1d Manuel Franceschini
    if doublecolons == 1:
641 8b312c1d Manuel Franceschini
      # We have a shorthand address, expand it
642 8b312c1d Manuel Franceschini
      parts = []
643 8b312c1d Manuel Franceschini
      twoparts = address.split("::")
644 d0c8c01d Iustin Pop
      sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":"))
645 d0c8c01d Iustin Pop
      parts = twoparts[0].split(":")
646 17385bd2 Andrea Spadaccini
      parts.extend(["0"] * (8 - sep))
647 d0c8c01d Iustin Pop
      parts += twoparts[1].split(":")
648 8b312c1d Manuel Franceschini
    else:
649 8b312c1d Manuel Franceschini
      parts = address.split(":")
650 8b312c1d Manuel Franceschini
651 8b312c1d Manuel Franceschini
    address_int = 0
652 8b312c1d Manuel Franceschini
    for part in parts:
653 d0c8c01d Iustin Pop
      address_int = (address_int << 16) + int(part or "0", 16)
654 8b312c1d Manuel Franceschini
655 8b312c1d Manuel Franceschini
    return address_int
656 1a8337f2 Manuel Franceschini
657 7845b8c8 Manuel Franceschini
658 981732fb Manuel Franceschini
def FormatAddress(address, family=None):
659 1a8337f2 Manuel Franceschini
  """Format a socket address
660 1a8337f2 Manuel Franceschini

661 1a8337f2 Manuel Franceschini
  @type address: family specific (usually tuple)
662 1a8337f2 Manuel Franceschini
  @param address: address, as reported by this class
663 981732fb Manuel Franceschini
  @type family: integer
664 981732fb Manuel Franceschini
  @param family: socket family (one of socket.AF_*) or None
665 1a8337f2 Manuel Franceschini

666 1a8337f2 Manuel Franceschini
  """
667 981732fb Manuel Franceschini
  if family is None:
668 981732fb Manuel Franceschini
    try:
669 981732fb Manuel Franceschini
      family = IPAddress.GetAddressFamily(address[0])
670 981732fb Manuel Franceschini
    except errors.IPAddressError:
671 981732fb Manuel Franceschini
      raise errors.ParameterError(address)
672 981732fb Manuel Franceschini
673 1a8337f2 Manuel Franceschini
  if family == socket.AF_UNIX and len(address) == 3:
674 1a8337f2 Manuel Franceschini
    return "pid=%s, uid=%s, gid=%s" % address
675 1a8337f2 Manuel Franceschini
676 1a8337f2 Manuel Franceschini
  if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2:
677 1a8337f2 Manuel Franceschini
    host, port = address
678 1a8337f2 Manuel Franceschini
    if family == socket.AF_INET6:
679 1a8337f2 Manuel Franceschini
      res = "[%s]" % host
680 1a8337f2 Manuel Franceschini
    else:
681 1a8337f2 Manuel Franceschini
      res = host
682 1a8337f2 Manuel Franceschini
683 1a8337f2 Manuel Franceschini
    if port is not None:
684 1a8337f2 Manuel Franceschini
      res += ":%s" % port
685 1a8337f2 Manuel Franceschini
686 1a8337f2 Manuel Franceschini
    return res
687 1a8337f2 Manuel Franceschini
688 1a8337f2 Manuel Franceschini
  raise errors.ParameterError(family, address)