Package ganeti :: Module netutils
[hide private]
[frames] | no frames]

Source Code for Module ganeti.netutils

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2010 Google Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 14  # General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the Free Software 
 18  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
 19  # 02110-1301, USA. 
 20   
 21   
 22  """Ganeti network utility module. 
 23   
 24  This module holds functions that can be used in both daemons (all) and 
 25  the command line scripts. 
 26   
 27  """ 
 28   
 29   
 30  import errno 
 31  import re 
 32  import socket 
 33  import struct 
 34  import IN 
 35   
 36  from ganeti import constants 
 37  from ganeti import errors 
 38   
 39  # Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...): 
 40  # struct ucred { pid_t pid; uid_t uid; gid_t gid; }; 
 41  # 
 42  # The GNU C Library defines gid_t and uid_t to be "unsigned int" and 
 43  # pid_t to "int". 
 44  # 
 45  # IEEE Std 1003.1-2008: 
 46  # "nlink_t, uid_t, gid_t, and id_t shall be integer types" 
 47  # "blksize_t, pid_t, and ssize_t shall be signed integer types" 
 48  _STRUCT_UCRED = "iII" 
 49  _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED) 
50 51 52 -def GetSocketCredentials(sock):
53 """Returns the credentials of the foreign process connected to a socket. 54 55 @param sock: Unix socket 56 @rtype: tuple; (number, number, number) 57 @return: The PID, UID and GID of the connected foreign process. 58 59 """ 60 peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED, 61 _STRUCT_UCRED_SIZE) 62 return struct.unpack(_STRUCT_UCRED, peercred)
63
64 65 -def GetHostname(name=None, family=None):
66 """Returns a Hostname object. 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: in case of errors in resolving 75 76 """ 77 try: 78 return Hostname(name=name, family=family) 79 except errors.ResolverError, err: 80 raise errors.OpPrereqError("The given name (%s) does not resolve: %s" % 81 (err[0], err[2]), errors.ECODE_RESOLVER)
82
83 84 -class Hostname:
85 """Class implementing resolver and hostname functionality. 86 87 """ 88 _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$") 89
90 - def __init__(self, name=None, family=None):
91 """Initialize the host name object. 92 93 If the name argument is None, it will use this system's name. 94 95 @type family: int 96 @param family: AF_INET | AF_INET6 | None 97 @type name: str 98 @param name: hostname or None 99 100 """ 101 self.name = self.GetNormalizedName(self.GetFqdn(name)) 102 self.ip = self.GetIP(self.name, family=family)
103 104 @classmethod
105 - def GetSysName(cls):
106 """Legacy method the get the current system's name. 107 108 """ 109 return cls.GetFqdn()
110 111 @staticmethod
112 - def GetFqdn(hostname=None):
113 """Return fqdn. 114 115 If hostname is None the system's fqdn is returned. 116 117 @type hostname: str 118 @param hostname: name to be fqdn'ed 119 @rtype: str 120 @return: fqdn of given name, if it exists, unmodified name otherwise 121 122 """ 123 if hostname is None: 124 return socket.getfqdn() 125 else: 126 return socket.getfqdn(hostname)
127 128 @staticmethod
129 - def GetIP(hostname, family=None):
130 """Return IP address of given hostname. 131 132 Supports both IPv4 and IPv6. 133 134 @type hostname: str 135 @param hostname: hostname to look up 136 @type family: int 137 @param family: AF_INET | AF_INET6 | None 138 @rtype: str 139 @return: IP address 140 @raise errors.ResolverError: in case of errors in resolving 141 142 """ 143 try: 144 if family in (socket.AF_INET, socket.AF_INET6): 145 result = socket.getaddrinfo(hostname, None, family) 146 else: 147 result = socket.getaddrinfo(hostname, None) 148 except (socket.gaierror, socket.herror, socket.error), err: 149 # hostname not found in DNS, or other socket exception in the 150 # (code, description format) 151 raise errors.ResolverError(hostname, err.args[0], err.args[1]) 152 153 # getaddrinfo() returns a list of 5-tupes (family, socktype, proto, 154 # canonname, sockaddr). We return the first tuple's first address in 155 # sockaddr 156 try: 157 return result[0][4][0] 158 except IndexError, err: 159 raise errors.ResolverError("Unknown error in getaddrinfo(): %s" % err)
160 161 @classmethod
162 - def GetNormalizedName(cls, hostname):
163 """Validate and normalize the given hostname. 164 165 @attention: the validation is a bit more relaxed than the standards 166 require; most importantly, we allow underscores in names 167 @raise errors.OpPrereqError: when the name is not valid 168 169 """ 170 hostname = hostname.lower() 171 if (not cls._VALID_NAME_RE.match(hostname) or 172 # double-dots, meaning empty label 173 ".." in hostname or 174 # empty initial label 175 hostname.startswith(".")): 176 raise errors.OpPrereqError("Invalid hostname '%s'" % hostname, 177 errors.ECODE_INVAL) 178 if hostname.endswith("."): 179 hostname = hostname.rstrip(".") 180 return hostname
181
182 183 -def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
184 """Simple ping implementation using TCP connect(2). 185 186 Check if the given IP is reachable by doing attempting a TCP connect 187 to it. 188 189 @type target: str 190 @param target: the IP or hostname to ping 191 @type port: int 192 @param port: the port to connect to 193 @type timeout: int 194 @param timeout: the timeout on the connection attempt 195 @type live_port_needed: boolean 196 @param live_port_needed: whether a closed port will cause the 197 function to return failure, as if there was a timeout 198 @type source: str or None 199 @param source: if specified, will cause the connect to be made 200 from this specific source address; failures to bind other 201 than C{EADDRNOTAVAIL} will be ignored 202 203 """ 204 try: 205 family = IPAddress.GetAddressFamily(target) 206 except errors.GenericError: 207 return False 208 209 sock = socket.socket(family, socket.SOCK_STREAM) 210 success = False 211 212 if source is not None: 213 try: 214 sock.bind((source, 0)) 215 except socket.error, (errcode, _): 216 if errcode == errno.EADDRNOTAVAIL: 217 success = False 218 219 sock.settimeout(timeout) 220 221 try: 222 sock.connect((target, port)) 223 sock.close() 224 success = True 225 except socket.timeout: 226 success = False 227 except socket.error, (errcode, _): 228 success = (not live_port_needed) and (errcode == errno.ECONNREFUSED) 229 230 return success
231
232 233 -def GetDaemonPort(daemon_name):
234 """Get the daemon port for this cluster. 235 236 Note that this routine does not read a ganeti-specific file, but 237 instead uses C{socket.getservbyname} to allow pre-customization of 238 this parameter outside of Ganeti. 239 240 @type daemon_name: string 241 @param daemon_name: daemon name (in constants.DAEMONS_PORTS) 242 @rtype: int 243 244 """ 245 if daemon_name not in constants.DAEMONS_PORTS: 246 raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name) 247 248 (proto, default_port) = constants.DAEMONS_PORTS[daemon_name] 249 try: 250 port = socket.getservbyname(daemon_name, proto) 251 except socket.error: 252 port = default_port 253 254 return port
255
256 257 -class IPAddress(object):
258 """Class that represents an IP address. 259 260 """ 261 iplen = 0 262 family = None 263 loopback_cidr = None 264 265 @staticmethod
266 - def _GetIPIntFromString(address):
267 """Abstract method to please pylint. 268 269 """ 270 raise NotImplementedError
271 272 @classmethod
273 - def IsValid(cls, address):
274 """Validate a IP address. 275 276 @type address: str 277 @param address: IP address to be checked 278 @rtype: bool 279 @return: True if valid, False otherwise 280 281 """ 282 if cls.family is None: 283 try: 284 family = cls.GetAddressFamily(address) 285 except errors.IPAddressError: 286 return False 287 else: 288 family = cls.family 289 290 try: 291 socket.inet_pton(family, address) 292 return True 293 except socket.error: 294 return False
295 296 @classmethod
297 - def Own(cls, address):
298 """Check if the current host has the the given IP address. 299 300 This is done by trying to bind the given address. We return True if we 301 succeed or false if a socket.error is raised. 302 303 @type address: str 304 @param address: IP address to be checked 305 @rtype: bool 306 @return: True if we own the address, False otherwise 307 308 """ 309 if cls.family is None: 310 try: 311 family = cls.GetAddressFamily(address) 312 except errors.IPAddressError: 313 return False 314 else: 315 family = cls.family 316 317 s = socket.socket(family, socket.SOCK_DGRAM) 318 success = False 319 try: 320 try: 321 s.bind((address, 0)) 322 success = True 323 except socket.error: 324 success = False 325 finally: 326 s.close() 327 return success
328 329 @classmethod
330 - def InNetwork(cls, cidr, address):
331 """Determine whether an address is within a network. 332 333 @type cidr: string 334 @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64' 335 @type address: str 336 @param address: IP address 337 @rtype: bool 338 @return: True if address is in cidr, False otherwise 339 340 """ 341 address_int = cls._GetIPIntFromString(address) 342 subnet = cidr.split("/") 343 assert len(subnet) == 2 344 try: 345 prefix = int(subnet[1]) 346 except ValueError: 347 return False 348 349 assert 0 <= prefix <= cls.iplen 350 target_int = cls._GetIPIntFromString(subnet[0]) 351 # Convert prefix netmask to integer value of netmask 352 netmask_int = (2**cls.iplen)-1 ^ ((2**cls.iplen)-1 >> prefix) 353 # Calculate hostmask 354 hostmask_int = netmask_int ^ (2**cls.iplen)-1 355 # Calculate network address by and'ing netmask 356 network_int = target_int & netmask_int 357 # Calculate broadcast address by or'ing hostmask 358 broadcast_int = target_int | hostmask_int 359 360 return network_int <= address_int <= broadcast_int
361 362 @staticmethod
363 - def GetAddressFamily(address):
364 """Get the address family of the given address. 365 366 @type address: str 367 @param address: ip address whose family will be returned 368 @rtype: int 369 @return: socket.AF_INET or socket.AF_INET6 370 @raise errors.GenericError: for invalid addresses 371 372 """ 373 try: 374 return IP4Address(address).family 375 except errors.IPAddressError: 376 pass 377 378 try: 379 return IP6Address(address).family 380 except errors.IPAddressError: 381 pass 382 383 raise errors.IPAddressError("Invalid address '%s'" % address)
384 385 @classmethod
386 - def IsLoopback(cls, address):
387 """Determine whether it is a loopback address. 388 389 @type address: str 390 @param address: IP address to be checked 391 @rtype: bool 392 @return: True if loopback, False otherwise 393 394 """ 395 try: 396 return cls.InNetwork(cls.loopback_cidr, address) 397 except errors.IPAddressError: 398 return False
399
400 401 -class IP4Address(IPAddress):
402 """IPv4 address class. 403 404 """ 405 iplen = 32 406 family = socket.AF_INET 407 loopback_cidr = "127.0.0.0/8" 408
409 - def __init__(self, address):
410 """Constructor for IPv4 address. 411 412 @type address: str 413 @param address: IP address 414 @raises errors.IPAddressError: if address invalid 415 416 """ 417 IPAddress.__init__(self) 418 if not self.IsValid(address): 419 raise errors.IPAddressError("IPv4 Address %s invalid" % address) 420 421 self.address = address
422 423 @staticmethod
424 - def _GetIPIntFromString(address):
425 """Get integer value of IPv4 address. 426 427 @type address: str 428 @param address: IPv6 address 429 @rtype: int 430 @return: integer value of given IP address 431 432 """ 433 address_int = 0 434 parts = address.split(".") 435 assert len(parts) == 4 436 for part in parts: 437 address_int = (address_int << 8) | int(part) 438 439 return address_int
440
441 442 -class IP6Address(IPAddress):
443 """IPv6 address class. 444 445 """ 446 iplen = 128 447 family = socket.AF_INET6 448 loopback_cidr = "::1/128" 449
450 - def __init__(self, address):
451 """Constructor for IPv6 address. 452 453 @type address: str 454 @param address: IP address 455 @raises errors.IPAddressError: if address invalid 456 457 """ 458 IPAddress.__init__(self) 459 if not self.IsValid(address): 460 raise errors.IPAddressError("IPv6 Address [%s] invalid" % address) 461 self.address = address
462 463 @staticmethod
464 - def _GetIPIntFromString(address):
465 """Get integer value of IPv6 address. 466 467 @type address: str 468 @param address: IPv6 address 469 @rtype: int 470 @return: integer value of given IP address 471 472 """ 473 doublecolons = address.count("::") 474 assert not doublecolons > 1 475 if doublecolons == 1: 476 # We have a shorthand address, expand it 477 parts = [] 478 twoparts = address.split("::") 479 sep = len(twoparts[0].split(':')) + len(twoparts[1].split(':')) 480 parts = twoparts[0].split(':') 481 [parts.append("0") for _ in range(8 - sep)] 482 parts += twoparts[1].split(':') 483 else: 484 parts = address.split(":") 485 486 address_int = 0 487 for part in parts: 488 address_int = (address_int << 16) + int(part or '0', 16) 489 490 return address_int
491
492 493 -def FormatAddress(address, family=None):
494 """Format a socket address 495 496 @type address: family specific (usually tuple) 497 @param address: address, as reported by this class 498 @type family: integer 499 @param family: socket family (one of socket.AF_*) or None 500 501 """ 502 if family is None: 503 try: 504 family = IPAddress.GetAddressFamily(address[0]) 505 except errors.IPAddressError: 506 raise errors.ParameterError(address) 507 508 if family == socket.AF_UNIX and len(address) == 3: 509 return "pid=%s, uid=%s, gid=%s" % address 510 511 if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2: 512 host, port = address 513 if family == socket.AF_INET6: 514 res = "[%s]" % host 515 else: 516 res = host 517 518 if port is not None: 519 res += ":%s" % port 520 521 return res 522 523 raise errors.ParameterError(family, address)
524