Revision a744b676 lib/utils.py

b/lib/utils.py
49 49
import calendar
50 50
import hmac
51 51
import collections
52
import struct
53
import IN
54 52

  
55 53
from cStringIO import StringIO
56 54

  
......
63 61
from ganeti import errors
64 62
from ganeti import constants
65 63
from ganeti import compat
64
from ganeti import netutils
66 65

  
67 66

  
68 67
_locksheld = []
......
84 83

  
85 84
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
86 85

  
87
# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
88
# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
89
#
90
# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
91
# pid_t to "int".
92
#
93
# IEEE Std 1003.1-2008:
94
# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
95
# "blksize_t, pid_t, and ssize_t shall be signed integer types"
96
_STRUCT_UCRED = "iII"
97
_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
98

  
99 86
# Certificate verification results
100 87
(CERT_WARNING,
101 88
 CERT_ERROR) = range(1, 3)
......
616 603
  return rr
617 604

  
618 605

  
619
def GetSocketCredentials(sock):
620
  """Returns the credentials of the foreign process connected to a socket.
621

  
622
  @param sock: Unix socket
623
  @rtype: tuple; (number, number, number)
624
  @return: The PID, UID and GID of the connected foreign process.
625

  
626
  """
627
  peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
628
                             _STRUCT_UCRED_SIZE)
629
  return struct.unpack(_STRUCT_UCRED, peercred)
630

  
631

  
632 606
def RemoveFile(filename):
633 607
  """Remove a file ignoring some errors.
634 608

  
......
1080 1054
  return None
1081 1055

  
1082 1056

  
1083
class HostInfo:
1084
  """Class implementing resolver and hostname functionality
1085

  
1086
  """
1087
  _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
1088

  
1089
  def __init__(self, name=None):
1090
    """Initialize the host name object.
1091

  
1092
    If the name argument is not passed, it will use this system's
1093
    name.
1094

  
1095
    """
1096
    if name is None:
1097
      name = self.SysName()
1098

  
1099
    self.query = name
1100
    self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
1101
    self.ip = self.ipaddrs[0]
1102

  
1103
  def ShortName(self):
1104
    """Returns the hostname without domain.
1105

  
1106
    """
1107
    return self.name.split('.')[0]
1108

  
1109
  @staticmethod
1110
  def SysName():
1111
    """Return the current system's name.
1112

  
1113
    This is simply a wrapper over C{socket.gethostname()}.
1114

  
1115
    """
1116
    return socket.gethostname()
1117

  
1118
  @staticmethod
1119
  def LookupHostname(hostname):
1120
    """Look up hostname
1121

  
1122
    @type hostname: str
1123
    @param hostname: hostname to look up
1124

  
1125
    @rtype: tuple
1126
    @return: a tuple (name, aliases, ipaddrs) as returned by
1127
        C{socket.gethostbyname_ex}
1128
    @raise errors.ResolverError: in case of errors in resolving
1129

  
1130
    """
1131
    try:
1132
      result = socket.gethostbyname_ex(hostname)
1133
    except (socket.gaierror, socket.herror, socket.error), err:
1134
      # hostname not found in DNS, or other socket exception in the
1135
      # (code, description format)
1136
      raise errors.ResolverError(hostname, err.args[0], err.args[1])
1137

  
1138
    return result
1139

  
1140
  @classmethod
1141
  def NormalizeName(cls, hostname):
1142
    """Validate and normalize the given hostname.
1143

  
1144
    @attention: the validation is a bit more relaxed than the standards
1145
        require; most importantly, we allow underscores in names
1146
    @raise errors.OpPrereqError: when the name is not valid
1147

  
1148
    """
1149
    hostname = hostname.lower()
1150
    if (not cls._VALID_NAME_RE.match(hostname) or
1151
        # double-dots, meaning empty label
1152
        ".." in hostname or
1153
        # empty initial label
1154
        hostname.startswith(".")):
1155
      raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
1156
                                 errors.ECODE_INVAL)
1157
    if hostname.endswith("."):
1158
      hostname = hostname.rstrip(".")
1159
    return hostname
1160

  
1161

  
1162 1057
def ValidateServiceName(name):
1163 1058
  """Validate the given service name.
1164 1059

  
......
1183 1078
  return name
1184 1079

  
1185 1080

  
1186
def GetHostInfo(name=None):
1187
  """Lookup host name and raise an OpPrereqError for failures"""
1188

  
1189
  try:
1190
    return HostInfo(name)
1191
  except errors.ResolverError, err:
1192
    raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
1193
                               (err[0], err[2]), errors.ECODE_RESOLVER)
1194

  
1195

  
1196 1081
def ListVolumeGroups():
1197 1082
  """List volume groups and their size
1198 1083

  
......
1292 1177
  return nv
1293 1178

  
1294 1179

  
1295
def _GenericIsValidIP(family, ip):
1296
  """Generic internal version of ip validation.
1297

  
1298
  @type family: int
1299
  @param family: socket.AF_INET | socket.AF_INET6
1300
  @type ip: str
1301
  @param ip: the address to be checked
1302
  @rtype: boolean
1303
  @return: True if ip is valid, False otherwise
1304

  
1305
  """
1306
  try:
1307
    socket.inet_pton(family, ip)
1308
    return True
1309
  except socket.error:
1310
    return False
1311

  
1312

  
1313
def IsValidIP4(ip):
1314
  """Verifies an IPv4 address.
1315

  
1316
  This function checks if the given address is a valid IPv4 address.
1317

  
1318
  @type ip: str
1319
  @param ip: the address to be checked
1320
  @rtype: boolean
1321
  @return: True if ip is valid, False otherwise
1322

  
1323
  """
1324
  return _GenericIsValidIP(socket.AF_INET, ip)
1325

  
1326

  
1327
def IsValidIP6(ip):
1328
  """Verifies an IPv6 address.
1329

  
1330
  This function checks if the given address is a valid IPv6 address.
1331

  
1332
  @type ip: str
1333
  @param ip: the address to be checked
1334
  @rtype: boolean
1335
  @return: True if ip is valid, False otherwise
1336

  
1337
  """
1338
  return _GenericIsValidIP(socket.AF_INET6, ip)
1339

  
1340

  
1341
def IsValidIP(ip):
1342
  """Verifies an IP address.
1343

  
1344
  This function checks if the given IP address (both IPv4 and IPv6) is valid.
1345

  
1346
  @type ip: str
1347
  @param ip: the address to be checked
1348
  @rtype: boolean
1349
  @return: True if ip is valid, False otherwise
1350

  
1351
  """
1352
  return IsValidIP4(ip) or IsValidIP6(ip)
1353

  
1354

  
1355
def GetAddressFamily(ip):
1356
  """Get the address family of the given address.
1357

  
1358
  @type ip: str
1359
  @param ip: ip address whose family will be returned
1360
  @rtype: int
1361
  @return: socket.AF_INET or socket.AF_INET6
1362
  @raise errors.GenericError: for invalid addresses
1363

  
1364
  """
1365
  if IsValidIP6(ip):
1366
    return socket.AF_INET6
1367
  elif IsValidIP4(ip):
1368
    return socket.AF_INET
1369
  else:
1370
    raise errors.GenericError("Address %s not valid" % ip)
1371

  
1372

  
1373 1180
def IsValidShellParam(word):
1374 1181
  """Verifies is the given word is safe from the shell's p.o.v.
1375 1182

  
......
1609 1416
      L{constants.ETC_HOSTS}
1610 1417

  
1611 1418
  """
1612
  hi = HostInfo(name=hostname)
1419
  hi = netutils.HostInfo(name=hostname)
1613 1420
  SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
1614 1421

  
1615 1422

  
......
1666 1473
      L{constants.ETC_HOSTS}
1667 1474

  
1668 1475
  """
1669
  hi = HostInfo(name=hostname)
1476
  hi = netutils.HostInfo(name=hostname)
1670 1477
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
1671 1478
  RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
1672 1479

  
......
1741 1548
  return ' '.join([ShellQuote(i) for i in args])
1742 1549

  
1743 1550

  
1744
def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
1745
  """Simple ping implementation using TCP connect(2).
1746

  
1747
  Check if the given IP is reachable by doing attempting a TCP connect
1748
  to it.
1749

  
1750
  @type target: str
1751
  @param target: the IP or hostname to ping
1752
  @type port: int
1753
  @param port: the port to connect to
1754
  @type timeout: int
1755
  @param timeout: the timeout on the connection attempt
1756
  @type live_port_needed: boolean
1757
  @param live_port_needed: whether a closed port will cause the
1758
      function to return failure, as if there was a timeout
1759
  @type source: str or None
1760
  @param source: if specified, will cause the connect to be made
1761
      from this specific source address; failures to bind other
1762
      than C{EADDRNOTAVAIL} will be ignored
1763

  
1764
  """
1765
  try:
1766
    family = GetAddressFamily(target)
1767
  except errors.GenericError:
1768
    return False
1769

  
1770
  sock = socket.socket(family, socket.SOCK_STREAM)
1771
  success = False
1772

  
1773
  if source is not None:
1774
    try:
1775
      sock.bind((source, 0))
1776
    except socket.error, (errcode, _):
1777
      if errcode == errno.EADDRNOTAVAIL:
1778
        success = False
1779

  
1780
  sock.settimeout(timeout)
1781

  
1782
  try:
1783
    sock.connect((target, port))
1784
    sock.close()
1785
    success = True
1786
  except socket.timeout:
1787
    success = False
1788
  except socket.error, (errcode, _):
1789
    success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
1790

  
1791
  return success
1792

  
1793

  
1794
def OwnIpAddress(address):
1795
  """Check if the current host has the the given IP address.
1796

  
1797
  This is done by trying to bind the given address. We return True if we
1798
  succeed or false if a socket.error is raised.
1799

  
1800
  @type address: string
1801
  @param address: the address to check
1802
  @rtype: bool
1803
  @return: True if we own the address
1804

  
1805
  """
1806
  family = GetAddressFamily(address)
1807
  s = socket.socket(family, socket.SOCK_DGRAM)
1808
  success = False
1809
  try:
1810
    try:
1811
      s.bind((address, 0))
1812
      success = True
1813
    except socket.error:
1814
      success = False
1815
  finally:
1816
    s.close()
1817
  return success
1818

  
1819

  
1820 1551
def ListVisibleFiles(path):
1821 1552
  """Returns a list of visible files in a directory.
1822 1553

  
......
2579 2310
  return float(seconds) + (float(microseconds) * 0.000001)
2580 2311

  
2581 2312

  
2582
def GetDaemonPort(daemon_name):
2583
  """Get the daemon port for this cluster.
2584

  
2585
  Note that this routine does not read a ganeti-specific file, but
2586
  instead uses C{socket.getservbyname} to allow pre-customization of
2587
  this parameter outside of Ganeti.
2588

  
2589
  @type daemon_name: string
2590
  @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
2591
  @rtype: int
2592

  
2593
  """
2594
  if daemon_name not in constants.DAEMONS_PORTS:
2595
    raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
2596

  
2597
  (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
2598
  try:
2599
    port = socket.getservbyname(daemon_name, proto)
2600
  except socket.error:
2601
    port = default_port
2602

  
2603
  return port
2604

  
2605

  
2606 2313
class LogFileHandler(logging.FileHandler):
2607 2314
  """Log handler that doesn't fallback to stderr.
2608 2315

  

Also available in: Unified diff