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

Source Code for Module ganeti.netutils

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2010, 2011, 2012 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30   
 31  """Ganeti network utility module. 
 32   
 33  This module holds functions that can be used in both daemons (all) and 
 34  the command line scripts. 
 35   
 36  """ 
 37   
 38   
 39  import errno 
 40  import os 
 41  import re 
 42  import socket 
 43  import struct 
 44  import IN 
 45  import logging 
 46   
 47  from ganeti import constants 
 48  from ganeti import errors 
 49  from ganeti import utils 
 50  from ganeti import vcluster 
 51   
 52  # Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...): 
 53  # struct ucred { pid_t pid; uid_t uid; gid_t gid; }; 
 54  # 
 55  # The GNU C Library defines gid_t and uid_t to be "unsigned int" and 
 56  # pid_t to "int". 
 57  # 
 58  # IEEE Std 1003.1-2008: 
 59  # "nlink_t, uid_t, gid_t, and id_t shall be integer types" 
 60  # "blksize_t, pid_t, and ssize_t shall be signed integer types" 
 61  _STRUCT_UCRED = "iII" 
 62  _STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED) 
 63   
 64  # Workaround a bug in some linux distributions that don't define SO_PEERCRED 
 65  try: 
 66    # pylint: disable=E1101 
 67    _SO_PEERCRED = IN.SO_PEERCRED 
 68  except AttributeError: 
 69    _SO_PEERCRED = 17 
 70   
 71  # Regexes used to find IP addresses in the output of ip. 
 72  _IP_RE_TEXT = r"[.:a-z0-9]+"      # separate for testing purposes 
 73  _IP_FAMILY_RE = re.compile(r"(?P<family>inet6?)\s+(?P<ip>%s)/" % _IP_RE_TEXT, 
 74                             re.IGNORECASE) 
 75   
 76  # Dict used to convert from a string representing an IP family to an IP 
 77  # version 
 78  _NAME_TO_IP_VER = { 
 79    "inet": constants.IP4_VERSION, 
 80    "inet6": constants.IP6_VERSION, 
 81    } 
82 83 84 -def _GetIpAddressesFromIpOutput(ip_output):
85 """Parses the output of the ip command and retrieves the IP addresses and 86 version. 87 88 @param ip_output: string containing the output of the ip command; 89 @rtype: dict; (int, list) 90 @return: a dict having as keys the IP versions and as values the 91 corresponding list of addresses found in the IP output. 92 93 """ 94 addr = dict((i, []) for i in _NAME_TO_IP_VER.values()) 95 96 for row in ip_output.splitlines(): 97 match = _IP_FAMILY_RE.search(row) 98 if match and IPAddress.IsValid(match.group("ip")): 99 addr[_NAME_TO_IP_VER[match.group("family")]].append(match.group("ip")) 100 101 return addr
102
103 104 -def GetSocketCredentials(sock):
105 """Returns the credentials of the foreign process connected to a socket. 106 107 @param sock: Unix socket 108 @rtype: tuple; (number, number, number) 109 @return: The PID, UID and GID of the connected foreign process. 110 111 """ 112 peercred = sock.getsockopt(socket.SOL_SOCKET, _SO_PEERCRED, 113 _STRUCT_UCRED_SIZE) 114 return struct.unpack(_STRUCT_UCRED, peercred)
115
116 117 -def IsValidInterface(ifname):
118 """Validate an interface name. 119 120 @type ifname: string 121 @param ifname: Name of the network interface 122 @return: boolean indicating whether the interface name is valid or not. 123 124 """ 125 return os.path.exists(utils.PathJoin("/sys/class/net", ifname))
126
127 128 -def GetInterfaceIpAddresses(ifname):
129 """Returns the IP addresses associated to the interface. 130 131 @type ifname: string 132 @param ifname: Name of the network interface 133 @return: A dict having for keys the IP version (either 134 L{constants.IP4_VERSION} or L{constants.IP6_VERSION}) and for 135 values the lists of IP addresses of the respective version 136 associated to the interface 137 138 """ 139 result = utils.RunCmd([constants.IP_COMMAND_PATH, "-o", "addr", "show", 140 ifname]) 141 142 if result.failed: 143 logging.error("Error running the ip command while getting the IP" 144 " addresses of %s", ifname) 145 return None 146 147 return _GetIpAddressesFromIpOutput(result.output)
148
149 150 -def GetHostname(name=None, family=None):
151 """Returns a Hostname object. 152 153 @type name: str 154 @param name: hostname or None 155 @type family: int 156 @param family: AF_INET | AF_INET6 | None 157 @rtype: L{Hostname} 158 @return: Hostname object 159 @raise errors.OpPrereqError: in case of errors in resolving 160 161 """ 162 try: 163 return Hostname(name=name, family=family) 164 except errors.ResolverError, err: 165 raise errors.OpPrereqError("The given name (%s) does not resolve: %s" % 166 (err[0], err[2]), errors.ECODE_RESOLVER)
167
168 169 -class Hostname(object):
170 """Class implementing resolver and hostname functionality. 171 172 """ 173 _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$") 174
175 - def __init__(self, name=None, family=None):
176 """Initialize the host name object. 177 178 If the name argument is None, it will use this system's name. 179 180 @type family: int 181 @param family: AF_INET | AF_INET6 | None 182 @type name: str 183 @param name: hostname or None 184 185 """ 186 self.name = self.GetFqdn(name) 187 self.ip = self.GetIP(self.name, family=family)
188 189 @classmethod
190 - def GetSysName(cls):
191 """Legacy method the get the current system's name. 192 193 """ 194 return cls.GetFqdn()
195 196 @classmethod
197 - def GetFqdn(cls, hostname=None):
198 """Return fqdn. 199 200 If hostname is None the system's fqdn is returned. 201 202 @type hostname: str 203 @param hostname: name to be fqdn'ed 204 @rtype: str 205 @return: fqdn of given name, if it exists, unmodified name otherwise 206 207 """ 208 if hostname is None: 209 virtfqdn = vcluster.GetVirtualHostname() 210 if virtfqdn: 211 result = virtfqdn 212 else: 213 result = socket.getfqdn() 214 else: 215 result = socket.getfqdn(hostname) 216 217 return cls.GetNormalizedName(result)
218 219 @staticmethod
220 - def GetIP(hostname, family=None):
221 """Return IP address of given hostname. 222 223 Supports both IPv4 and IPv6. 224 225 @type hostname: str 226 @param hostname: hostname to look up 227 @type family: int 228 @param family: AF_INET | AF_INET6 | None 229 @rtype: str 230 @return: IP address 231 @raise errors.ResolverError: in case of errors in resolving 232 233 """ 234 try: 235 if family in (socket.AF_INET, socket.AF_INET6): 236 result = socket.getaddrinfo(hostname, None, family) 237 else: 238 result = socket.getaddrinfo(hostname, None) 239 except (socket.gaierror, socket.herror, socket.error), err: 240 # hostname not found in DNS, or other socket exception in the 241 # (code, description format) 242 raise errors.ResolverError(hostname, err.args[0], err.args[1]) 243 244 # getaddrinfo() returns a list of 5-tupes (family, socktype, proto, 245 # canonname, sockaddr). We return the first tuple's first address in 246 # sockaddr 247 try: 248 return result[0][4][0] 249 except IndexError, err: 250 # we don't have here an actual error code, it's just that the 251 # data type returned by getaddrinfo is not what we expected; 252 # let's keep the same format in the exception arguments with a 253 # dummy error code 254 raise errors.ResolverError(hostname, 0, 255 "Unknown error in getaddrinfo(): %s" % err)
256 257 @classmethod
258 - def GetNormalizedName(cls, hostname):
259 """Validate and normalize the given hostname. 260 261 @attention: the validation is a bit more relaxed than the standards 262 require; most importantly, we allow underscores in names 263 @raise errors.OpPrereqError: when the name is not valid 264 265 """ 266 hostname = hostname.lower() 267 if (not cls._VALID_NAME_RE.match(hostname) or 268 # double-dots, meaning empty label 269 ".." in hostname or 270 # empty initial label 271 hostname.startswith(".")): 272 raise errors.OpPrereqError("Invalid hostname '%s'" % hostname, 273 errors.ECODE_INVAL) 274 if hostname.endswith("."): 275 hostname = hostname.rstrip(".") 276 return hostname
277
278 279 -def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
280 """Simple ping implementation using TCP connect(2). 281 282 Check if the given IP is reachable by doing attempting a TCP connect 283 to it. 284 285 @type target: str 286 @param target: the IP to ping 287 @type port: int 288 @param port: the port to connect to 289 @type timeout: int 290 @param timeout: the timeout on the connection attempt 291 @type live_port_needed: boolean 292 @param live_port_needed: whether a closed port will cause the 293 function to return failure, as if there was a timeout 294 @type source: str or None 295 @param source: if specified, will cause the connect to be made 296 from this specific source address; failures to bind other 297 than C{EADDRNOTAVAIL} will be ignored 298 299 """ 300 logging.debug("Attempting to reach TCP port %s on target %s with a timeout" 301 " of %s seconds", port, target, timeout) 302 303 try: 304 family = IPAddress.GetAddressFamily(target) 305 except errors.IPAddressError, err: 306 raise errors.ProgrammerError("Family of IP address given in parameter" 307 " 'target' can't be determined: %s" % err) 308 309 sock = socket.socket(family, socket.SOCK_STREAM) 310 success = False 311 312 if source is not None: 313 try: 314 sock.bind((source, 0)) 315 except socket.error, err: 316 if err[0] == errno.EADDRNOTAVAIL: 317 success = False 318 319 sock.settimeout(timeout) 320 321 try: 322 sock.connect((target, port)) 323 sock.close() 324 success = True 325 except socket.timeout: 326 success = False 327 except socket.error, err: 328 success = (not live_port_needed) and (err[0] == errno.ECONNREFUSED) 329 330 return success
331
332 333 -def GetDaemonPort(daemon_name):
334 """Get the daemon port for this cluster. 335 336 Note that this routine does not read a ganeti-specific file, but 337 instead uses C{socket.getservbyname} to allow pre-customization of 338 this parameter outside of Ganeti. 339 340 @type daemon_name: string 341 @param daemon_name: daemon name (in constants.DAEMONS_PORTS) 342 @rtype: int 343 344 """ 345 if daemon_name not in constants.DAEMONS_PORTS: 346 raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name) 347 348 (proto, default_port) = constants.DAEMONS_PORTS[daemon_name] 349 try: 350 port = socket.getservbyname(daemon_name, proto) 351 except socket.error: 352 port = default_port 353 354 return port
355
356 357 -class IPAddress(object):
358 """Class that represents an IP address. 359 360 """ 361 iplen = 0 362 family = None 363 loopback_cidr = None 364 365 @staticmethod
366 - def _GetIPIntFromString(address):
367 """Abstract method to please pylint. 368 369 """ 370 raise NotImplementedError
371 372 @classmethod
373 - def IsValid(cls, address):
374 """Validate a IP address. 375 376 @type address: str 377 @param address: IP address to be checked 378 @rtype: bool 379 @return: True if valid, False otherwise 380 381 """ 382 if cls.family is None: 383 try: 384 family = cls.GetAddressFamily(address) 385 except errors.IPAddressError: 386 return False 387 else: 388 family = cls.family 389 390 try: 391 socket.inet_pton(family, address) 392 return True 393 except socket.error: 394 return False
395 396 @classmethod
397 - def ValidateNetmask(cls, netmask):
398 """Validate a netmask suffix in CIDR notation. 399 400 @type netmask: int 401 @param netmask: netmask suffix to validate 402 @rtype: bool 403 @return: True if valid, False otherwise 404 405 """ 406 assert (isinstance(netmask, (int, long))) 407 408 return 0 < netmask <= cls.iplen
409 410 @classmethod
411 - def Own(cls, address):
412 """Check if the current host has the the given IP address. 413 414 This is done by trying to bind the given address. We return True if we 415 succeed or false if a socket.error is raised. 416 417 @type address: str 418 @param address: IP address to be checked 419 @rtype: bool 420 @return: True if we own the address, False otherwise 421 422 """ 423 if cls.family is None: 424 try: 425 family = cls.GetAddressFamily(address) 426 except errors.IPAddressError: 427 return False 428 else: 429 family = cls.family 430 431 s = socket.socket(family, socket.SOCK_DGRAM) 432 success = False 433 try: 434 try: 435 s.bind((address, 0)) 436 success = True 437 except socket.error: 438 success = False 439 finally: 440 s.close() 441 return success
442 443 @classmethod
444 - def InNetwork(cls, cidr, address):
445 """Determine whether an address is within a network. 446 447 @type cidr: string 448 @param cidr: Network in CIDR notation, e.g. '192.0.2.0/24', '2001:db8::/64' 449 @type address: str 450 @param address: IP address 451 @rtype: bool 452 @return: True if address is in cidr, False otherwise 453 454 """ 455 address_int = cls._GetIPIntFromString(address) 456 subnet = cidr.split("/") 457 assert len(subnet) == 2 458 try: 459 prefix = int(subnet[1]) 460 except ValueError: 461 return False 462 463 assert 0 <= prefix <= cls.iplen 464 target_int = cls._GetIPIntFromString(subnet[0]) 465 # Convert prefix netmask to integer value of netmask 466 netmask_int = (2 ** cls.iplen) - 1 ^ ((2 ** cls.iplen) - 1 >> prefix) 467 # Calculate hostmask 468 hostmask_int = netmask_int ^ (2 ** cls.iplen) - 1 469 # Calculate network address by and'ing netmask 470 network_int = target_int & netmask_int 471 # Calculate broadcast address by or'ing hostmask 472 broadcast_int = target_int | hostmask_int 473 474 return network_int <= address_int <= broadcast_int
475 476 @staticmethod
477 - def GetAddressFamily(address):
478 """Get the address family of the given address. 479 480 @type address: str 481 @param address: ip address whose family will be returned 482 @rtype: int 483 @return: C{socket.AF_INET} or C{socket.AF_INET6} 484 @raise errors.GenericError: for invalid addresses 485 486 """ 487 try: 488 return IP4Address(address).family 489 except errors.IPAddressError: 490 pass 491 492 try: 493 return IP6Address(address).family 494 except errors.IPAddressError: 495 pass 496 497 raise errors.IPAddressError("Invalid address '%s'" % address)
498 499 @staticmethod
500 - def GetVersionFromAddressFamily(family):
501 """Convert an IP address family to the corresponding IP version. 502 503 @type family: int 504 @param family: IP address family, one of socket.AF_INET or socket.AF_INET6 505 @return: an int containing the IP version, one of L{constants.IP4_VERSION} 506 or L{constants.IP6_VERSION} 507 @raise errors.ProgrammerError: for unknown families 508 509 """ 510 if family == socket.AF_INET: 511 return constants.IP4_VERSION 512 elif family == socket.AF_INET6: 513 return constants.IP6_VERSION 514 515 raise errors.ProgrammerError("%s is not a valid IP address family" % family)
516 517 @staticmethod
518 - def GetAddressFamilyFromVersion(version):
519 """Convert an IP version to the corresponding IP address family. 520 521 @type version: int 522 @param version: IP version, one of L{constants.IP4_VERSION} or 523 L{constants.IP6_VERSION} 524 @return: an int containing the IP address family, one of C{socket.AF_INET} 525 or C{socket.AF_INET6} 526 @raise errors.ProgrammerError: for unknown IP versions 527 528 """ 529 if version == constants.IP4_VERSION: 530 return socket.AF_INET 531 elif version == constants.IP6_VERSION: 532 return socket.AF_INET6 533 534 raise errors.ProgrammerError("%s is not a valid IP version" % version)
535 536 @staticmethod
537 - def GetClassFromIpVersion(version):
538 """Return the IPAddress subclass for the given IP version. 539 540 @type version: int 541 @param version: IP version, one of L{constants.IP4_VERSION} or 542 L{constants.IP6_VERSION} 543 @return: a subclass of L{netutils.IPAddress} 544 @raise errors.ProgrammerError: for unknowo IP versions 545 546 """ 547 if version == constants.IP4_VERSION: 548 return IP4Address 549 elif version == constants.IP6_VERSION: 550 return IP6Address 551 552 raise errors.ProgrammerError("%s is not a valid IP version" % version)
553 554 @staticmethod
555 - def GetClassFromIpFamily(family):
556 """Return the IPAddress subclass for the given IP family. 557 558 @param family: IP family (one of C{socket.AF_INET} or C{socket.AF_INET6} 559 @return: a subclass of L{netutils.IPAddress} 560 @raise errors.ProgrammerError: for unknowo IP versions 561 562 """ 563 return IPAddress.GetClassFromIpVersion( 564 IPAddress.GetVersionFromAddressFamily(family))
565 566 @classmethod
567 - def IsLoopback(cls, address):
568 """Determine whether it is a loopback address. 569 570 @type address: str 571 @param address: IP address to be checked 572 @rtype: bool 573 @return: True if loopback, False otherwise 574 575 """ 576 try: 577 return cls.InNetwork(cls.loopback_cidr, address) 578 except errors.IPAddressError: 579 return False
580
581 582 -class IP4Address(IPAddress):
583 """IPv4 address class. 584 585 """ 586 iplen = 32 587 family = socket.AF_INET 588 loopback_cidr = "127.0.0.0/8" 589
590 - def __init__(self, address):
591 """Constructor for IPv4 address. 592 593 @type address: str 594 @param address: IP address 595 @raises errors.IPAddressError: if address invalid 596 597 """ 598 IPAddress.__init__(self) 599 if not self.IsValid(address): 600 raise errors.IPAddressError("IPv4 Address %s invalid" % address) 601 602 self.address = address
603 604 @staticmethod
605 - def _GetIPIntFromString(address):
606 """Get integer value of IPv4 address. 607 608 @type address: str 609 @param address: IPv6 address 610 @rtype: int 611 @return: integer value of given IP address 612 613 """ 614 address_int = 0 615 parts = address.split(".") 616 assert len(parts) == 4 617 for part in parts: 618 address_int = (address_int << 8) | int(part) 619 620 return address_int
621
622 623 -class IP6Address(IPAddress):
624 """IPv6 address class. 625 626 """ 627 iplen = 128 628 family = socket.AF_INET6 629 loopback_cidr = "::1/128" 630
631 - def __init__(self, address):
632 """Constructor for IPv6 address. 633 634 @type address: str 635 @param address: IP address 636 @raises errors.IPAddressError: if address invalid 637 638 """ 639 IPAddress.__init__(self) 640 if not self.IsValid(address): 641 raise errors.IPAddressError("IPv6 Address [%s] invalid" % address) 642 self.address = address
643 644 @staticmethod
645 - def _GetIPIntFromString(address):
646 """Get integer value of IPv6 address. 647 648 @type address: str 649 @param address: IPv6 address 650 @rtype: int 651 @return: integer value of given IP address 652 653 """ 654 doublecolons = address.count("::") 655 assert not doublecolons > 1 656 if doublecolons == 1: 657 # We have a shorthand address, expand it 658 parts = [] 659 twoparts = address.split("::") 660 sep = len(twoparts[0].split(":")) + len(twoparts[1].split(":")) 661 parts = twoparts[0].split(":") 662 parts.extend(["0"] * (8 - sep)) 663 parts += twoparts[1].split(":") 664 else: 665 parts = address.split(":") 666 667 address_int = 0 668 for part in parts: 669 address_int = (address_int << 16) + int(part or "0", 16) 670 671 return address_int
672
673 674 -def FormatAddress(address, family=None):
675 """Format a socket address 676 677 @type address: family specific (usually tuple) 678 @param address: address, as reported by this class 679 @type family: integer 680 @param family: socket family (one of socket.AF_*) or None 681 682 """ 683 if family is None: 684 try: 685 family = IPAddress.GetAddressFamily(address[0]) 686 except errors.IPAddressError: 687 raise errors.ParameterError(address) 688 689 if family == socket.AF_UNIX and len(address) == 3: 690 return "pid=%s, uid=%s, gid=%s" % address 691 692 if family in (socket.AF_INET, socket.AF_INET6) and len(address) == 2: 693 host, port = address 694 if family == socket.AF_INET6: 695 res = "[%s]" % host 696 else: 697 res = host 698 699 if port is not None: 700 res += ":%s" % port 701 702 return res 703 704 raise errors.ParameterError(family, address)
705