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