import calendar
import hmac
import collections
-import struct
-import IN
from cStringIO import StringIO
from ganeti import errors
from ganeti import constants
from ganeti import compat
+from ganeti import netutils
_locksheld = []
_VALID_SERVICE_NAME_RE = re.compile("^[-_.a-zA-Z0-9]{1,128}$")
-# Structure definition for getsockopt(SOL_SOCKET, SO_PEERCRED, ...):
-# struct ucred { pid_t pid; uid_t uid; gid_t gid; };
-#
-# The GNU C Library defines gid_t and uid_t to be "unsigned int" and
-# pid_t to "int".
-#
-# IEEE Std 1003.1-2008:
-# "nlink_t, uid_t, gid_t, and id_t shall be integer types"
-# "blksize_t, pid_t, and ssize_t shall be signed integer types"
-_STRUCT_UCRED = "iII"
-_STRUCT_UCRED_SIZE = struct.calcsize(_STRUCT_UCRED)
-
# Certificate verification results
(CERT_WARNING,
CERT_ERROR) = range(1, 3)
return rr
-def GetSocketCredentials(sock):
- """Returns the credentials of the foreign process connected to a socket.
-
- @param sock: Unix socket
- @rtype: tuple; (number, number, number)
- @return: The PID, UID and GID of the connected foreign process.
-
- """
- peercred = sock.getsockopt(socket.SOL_SOCKET, IN.SO_PEERCRED,
- _STRUCT_UCRED_SIZE)
- return struct.unpack(_STRUCT_UCRED, peercred)
-
-
def RemoveFile(filename):
"""Remove a file ignoring some errors.
return None
-class HostInfo:
- """Class implementing resolver and hostname functionality
-
- """
- _VALID_NAME_RE = re.compile("^[a-z0-9._-]{1,255}$")
-
- def __init__(self, name=None):
- """Initialize the host name object.
-
- If the name argument is not passed, it will use this system's
- name.
-
- """
- if name is None:
- name = self.SysName()
-
- self.query = name
- self.name, self.aliases, self.ipaddrs = self.LookupHostname(name)
- self.ip = self.ipaddrs[0]
-
- def ShortName(self):
- """Returns the hostname without domain.
-
- """
- return self.name.split('.')[0]
-
- @staticmethod
- def SysName():
- """Return the current system's name.
-
- This is simply a wrapper over C{socket.gethostname()}.
-
- """
- return socket.gethostname()
-
- @staticmethod
- def LookupHostname(hostname):
- """Look up hostname
-
- @type hostname: str
- @param hostname: hostname to look up
-
- @rtype: tuple
- @return: a tuple (name, aliases, ipaddrs) as returned by
- C{socket.gethostbyname_ex}
- @raise errors.ResolverError: in case of errors in resolving
-
- """
- try:
- result = socket.gethostbyname_ex(hostname)
- except (socket.gaierror, socket.herror, socket.error), err:
- # hostname not found in DNS, or other socket exception in the
- # (code, description format)
- raise errors.ResolverError(hostname, err.args[0], err.args[1])
-
- return result
-
- @classmethod
- def NormalizeName(cls, hostname):
- """Validate and normalize the given hostname.
-
- @attention: the validation is a bit more relaxed than the standards
- require; most importantly, we allow underscores in names
- @raise errors.OpPrereqError: when the name is not valid
-
- """
- hostname = hostname.lower()
- if (not cls._VALID_NAME_RE.match(hostname) or
- # double-dots, meaning empty label
- ".." in hostname or
- # empty initial label
- hostname.startswith(".")):
- raise errors.OpPrereqError("Invalid hostname '%s'" % hostname,
- errors.ECODE_INVAL)
- if hostname.endswith("."):
- hostname = hostname.rstrip(".")
- return hostname
-
-
def ValidateServiceName(name):
"""Validate the given service name.
return name
-def GetHostInfo(name=None):
- """Lookup host name and raise an OpPrereqError for failures"""
-
- try:
- return HostInfo(name)
- except errors.ResolverError, err:
- raise errors.OpPrereqError("The given name (%s) does not resolve: %s" %
- (err[0], err[2]), errors.ECODE_RESOLVER)
-
-
def ListVolumeGroups():
"""List volume groups and their size
return nv
-def _GenericIsValidIP(family, ip):
- """Generic internal version of ip validation.
-
- @type family: int
- @param family: socket.AF_INET | socket.AF_INET6
- @type ip: str
- @param ip: the address to be checked
- @rtype: boolean
- @return: True if ip is valid, False otherwise
-
- """
- try:
- socket.inet_pton(family, ip)
- return True
- except socket.error:
- return False
-
-
-def IsValidIP4(ip):
- """Verifies an IPv4 address.
-
- This function checks if the given address is a valid IPv4 address.
-
- @type ip: str
- @param ip: the address to be checked
- @rtype: boolean
- @return: True if ip is valid, False otherwise
-
- """
- return _GenericIsValidIP(socket.AF_INET, ip)
-
-
-def IsValidIP6(ip):
- """Verifies an IPv6 address.
-
- This function checks if the given address is a valid IPv6 address.
-
- @type ip: str
- @param ip: the address to be checked
- @rtype: boolean
- @return: True if ip is valid, False otherwise
-
- """
- return _GenericIsValidIP(socket.AF_INET6, ip)
-
-
-def IsValidIP(ip):
- """Verifies an IP address.
-
- This function checks if the given IP address (both IPv4 and IPv6) is valid.
-
- @type ip: str
- @param ip: the address to be checked
- @rtype: boolean
- @return: True if ip is valid, False otherwise
-
- """
- return IsValidIP4(ip) or IsValidIP6(ip)
-
-
-def GetAddressFamily(ip):
- """Get the address family of the given address.
-
- @type ip: str
- @param ip: ip address whose family will be returned
- @rtype: int
- @return: socket.AF_INET or socket.AF_INET6
- @raise errors.GenericError: for invalid addresses
-
- """
- if IsValidIP6(ip):
- return socket.AF_INET6
- elif IsValidIP4(ip):
- return socket.AF_INET
- else:
- raise errors.GenericError("Address %s not valid" % ip)
-
-
def IsValidShellParam(word):
"""Verifies is the given word is safe from the shell's p.o.v.
return value
+def ParseCpuMask(cpu_mask):
+ """Parse a CPU mask definition and return the list of CPU IDs.
+
+ CPU mask format: comma-separated list of CPU IDs
+ or dash-separated ID ranges
+ Example: "0-2,5" -> "0,1,2,5"
+
+ @type cpu_mask: str
+ @param cpu_mask: CPU mask definition
+ @rtype: list of int
+ @return: list of CPU IDs
+
+ """
+ if not cpu_mask:
+ return []
+ cpu_list = []
+ for range_def in cpu_mask.split(","):
+ boundaries = range_def.split("-")
+ n_elements = len(boundaries)
+ if n_elements > 2:
+ raise errors.ParseError("Invalid CPU ID range definition"
+ " (only one hyphen allowed): %s" % range_def)
+ try:
+ lower = int(boundaries[0])
+ except (ValueError, TypeError), err:
+ raise errors.ParseError("Invalid CPU ID value for lower boundary of"
+ " CPU ID range: %s" % str(err))
+ try:
+ higher = int(boundaries[-1])
+ except (ValueError, TypeError), err:
+ raise errors.ParseError("Invalid CPU ID value for higher boundary of"
+ " CPU ID range: %s" % str(err))
+ if lower > higher:
+ raise errors.ParseError("Invalid CPU ID range definition"
+ " (%d > %d): %s" % (lower, higher, range_def))
+ cpu_list.extend(range(lower, higher + 1))
+ return cpu_list
+
+
def AddAuthorizedKey(file_name, key):
"""Adds an SSH public key to an authorized_keys file.
L{constants.ETC_HOSTS}
"""
- hi = HostInfo(name=hostname)
+ hi = netutils.HostInfo(name=hostname)
SetEtcHostsEntry(constants.ETC_HOSTS, hi.ip, hi.name, [hi.ShortName()])
L{constants.ETC_HOSTS}
"""
- hi = HostInfo(name=hostname)
+ hi = netutils.HostInfo(name=hostname)
RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.name)
RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName())
return ' '.join([ShellQuote(i) for i in args])
-def TcpPing(target, port, timeout=10, live_port_needed=False, source=None):
- """Simple ping implementation using TCP connect(2).
-
- Check if the given IP is reachable by doing attempting a TCP connect
- to it.
-
- @type target: str
- @param target: the IP or hostname to ping
- @type port: int
- @param port: the port to connect to
- @type timeout: int
- @param timeout: the timeout on the connection attempt
- @type live_port_needed: boolean
- @param live_port_needed: whether a closed port will cause the
- function to return failure, as if there was a timeout
- @type source: str or None
- @param source: if specified, will cause the connect to be made
- from this specific source address; failures to bind other
- than C{EADDRNOTAVAIL} will be ignored
+class ShellWriter:
+ """Helper class to write scripts with indentation.
"""
- try:
- family = GetAddressFamily(target)
- except errors.GenericError:
- return False
+ INDENT_STR = " "
- sock = socket.socket(family, socket.SOCK_STREAM)
- success = False
+ def __init__(self, fh):
+ """Initializes this class.
- if source is not None:
- try:
- sock.bind((source, 0))
- except socket.error, (errcode, _):
- if errcode == errno.EADDRNOTAVAIL:
- success = False
+ """
+ self._fh = fh
+ self._indent = 0
- sock.settimeout(timeout)
+ def IncIndent(self):
+ """Increase indentation level by 1.
- try:
- sock.connect((target, port))
- sock.close()
- success = True
- except socket.timeout:
- success = False
- except socket.error, (errcode, _):
- success = (not live_port_needed) and (errcode == errno.ECONNREFUSED)
+ """
+ self._indent += 1
- return success
+ def DecIndent(self):
+ """Decrease indentation level by 1.
+ """
+ assert self._indent > 0
+ self._indent -= 1
-def OwnIpAddress(address):
- """Check if the current host has the the given IP address.
+ def Write(self, txt, *args):
+ """Write line to output file.
- This is done by trying to bind the given address. We return True if we
- succeed or false if a socket.error is raised.
+ """
+ assert self._indent >= 0
- @type address: string
- @param address: the address to check
- @rtype: bool
- @return: True if we own the address
+ self._fh.write(self._indent * self.INDENT_STR)
- """
- family = GetAddressFamily(address)
- s = socket.socket(family, socket.SOCK_DGRAM)
- success = False
- try:
- try:
- s.bind((address, 0))
- success = True
- except socket.error:
- success = False
- finally:
- s.close()
- return success
+ if args:
+ self._fh.write(txt % args)
+ else:
+ self._fh.write(txt)
+
+ self._fh.write("\n")
def ListVisibleFiles(path):
_CloseFDNoErr(fd)
-def Mlockall():
+def Mlockall(_ctypes=ctypes):
"""Lock current process' virtual address space into RAM.
This is equivalent to the C call mlockall(MCL_CURRENT|MCL_FUTURE),
see mlock(2) for more details. This function requires ctypes module.
+ @raises errors.NoCtypesError: if ctypes module is not found
+
"""
- if ctypes is None:
- logging.warning("Cannot set memory lock, ctypes module not found")
- return
+ if _ctypes is None:
+ raise errors.NoCtypesError()
- libc = ctypes.cdll.LoadLibrary("libc.so.6")
+ libc = _ctypes.cdll.LoadLibrary("libc.so.6")
if libc is None:
logging.error("Cannot set memory lock, ctypes cannot load libc")
return
# its value correctly, should the mlockall call fail, in order to see what
# the actual error code was.
# pylint: disable-msg=W0212
- libc.__errno_location.restype = ctypes.POINTER(ctypes.c_int)
+ libc.__errno_location.restype = _ctypes.POINTER(_ctypes.c_int)
if libc.mlockall(_MCL_CURRENT | _MCL_FUTURE):
# pylint: disable-msg=W0212
return float(seconds) + (float(microseconds) * 0.000001)
-def GetDaemonPort(daemon_name):
- """Get the daemon port for this cluster.
-
- Note that this routine does not read a ganeti-specific file, but
- instead uses C{socket.getservbyname} to allow pre-customization of
- this parameter outside of Ganeti.
-
- @type daemon_name: string
- @param daemon_name: daemon name (in constants.DAEMONS_PORTS)
- @rtype: int
-
- """
- if daemon_name not in constants.DAEMONS_PORTS:
- raise errors.ProgrammerError("Unknown daemon: %s" % daemon_name)
-
- (proto, default_port) = constants.DAEMONS_PORTS[daemon_name]
- try:
- port = socket.getservbyname(daemon_name, proto)
- except socket.error:
- port = default_port
-
- return port
-
-
class LogFileHandler(logging.FileHandler):
"""Log handler that doesn't fallback to stderr.
return BytesToMebibyte(size)
+def GetMounts(filename=constants.PROC_MOUNTS):
+ """Returns the list of mounted filesystems.
+
+ This function is Linux-specific.
+
+ @param filename: path of mounts file (/proc/mounts by default)
+ @rtype: list of tuples
+ @return: list of mount entries (device, mountpoint, fstype, options)
+
+ """
+ # TODO(iustin): investigate non-Linux options (e.g. via mount output)
+ data = []
+ mountlines = ReadFile(filename).splitlines()
+ for line in mountlines:
+ device, mountpoint, fstype, options, _ = line.split(None, 4)
+ data.append((device, mountpoint, fstype, options))
+
+ return data
+
+
def GetFilesystemStats(path):
"""Returns the total and free space on a filesystem.