"""Ganeti small utilities
+
"""
import shutil
import errno
import pwd
+import itertools
from ganeti import logger
from ganeti import errors
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.
key_fields = key.split()
fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
- out = os.fdopen(fd, 'w')
try:
- f = open(file_name, 'r')
+ out = os.fdopen(fd, 'w')
+ try:
+ f = open(file_name, 'r')
+ try:
+ for line in f:
+ # Ignore whitespace changes while comparing lines
+ if line.split() != key_fields:
+ out.write(line)
+
+ out.flush()
+ os.rename(tmpname, file_name)
+ finally:
+ f.close()
+ finally:
+ out.close()
+ except:
+ RemoveFile(tmpname)
+ raise
+
+
+def SetEtcHostsEntry(file_name, ip, hostname, aliases):
+ """Sets the name of an IP address and hostname in /etc/hosts.
+
+ """
+ fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
+ try:
+ out = os.fdopen(fd, 'w')
+ try:
+ f = open(file_name, 'r')
+ try:
+ written = False
+ for line in f:
+ fields = line.split()
+ if fields and not fields[0].startswith('#') and ip == fields[0]:
+ continue
+ out.write(line)
+
+ out.write("%s\t%s" % (ip, hostname))
+ if aliases:
+ out.write(" %s" % ' '.join(aliases))
+ out.write('\n')
+
+ out.flush()
+ os.fsync(out)
+ os.rename(tmpname, file_name)
+ finally:
+ f.close()
+ finally:
+ out.close()
+ except:
+ RemoveFile(tmpname)
+ raise
+
+
+def RemoveEtcHostsEntry(file_name, hostname):
+ """Removes a hostname from /etc/hosts.
+
+ IP addresses without names are removed from the file.
+ """
+ fd, tmpname = tempfile.mkstemp(dir=os.path.dirname(file_name))
+ try:
+ out = os.fdopen(fd, 'w')
try:
- for line in f:
- # Ignore whitespace changes while comparing lines
- if line.split() != key_fields:
+ f = open(file_name, 'r')
+ try:
+ for line in f:
+ fields = line.split()
+ if len(fields) > 1 and not fields[0].startswith('#'):
+ names = fields[1:]
+ if hostname in names:
+ while hostname in names:
+ names.remove(hostname)
+ if names:
+ out.write("%s %s\n" % (fields[0], ' '.join(names)))
+ continue
+
out.write(line)
- out.flush()
- os.rename(tmpname, file_name)
+ out.flush()
+ os.fsync(out)
+ os.rename(tmpname, file_name)
+ finally:
+ f.close()
finally:
- f.close()
- finally:
- out.close()
+ out.close()
+ except:
+ RemoveFile(tmpname)
+ raise
def CreateBackup(file_name):
file_name)
prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time()))
- dir = os.path.dirname(file_name)
+ dir_name = os.path.dirname(file_name)
fsrc = open(file_name, 'rb')
try:
- (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir)
+ (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name)
fdst = os.fdopen(fd, 'wb')
try:
shutil.copyfileobj(fsrc, fdst)
return [i for i in os.listdir(path) if not i.startswith(".")]
-def GetHomeDir(uid, default=None):
- """Try to get the homedir of the given user id.
+def GetHomeDir(user, default=None):
+ """Try to get the homedir of the given user.
+
+ The user can be passed either as a string (denoting the name) or as
+ an integer (denoting the user id). If the user is not found, the
+ 'default' argument is returned, which defaults to None.
"""
try:
- result = pwd.getpwuid(uid)
+ if isinstance(user, basestring):
+ result = pwd.getpwnam(user)
+ elif isinstance(user, (int, long)):
+ result = pwd.getpwuid(user)
+ else:
+ raise errors.ProgrammerError("Invalid type passed to GetHomeDir (%s)" %
+ type(user))
except KeyError:
return default
return result.pw_dir
+
+
+def NewUUID():
+ """Returns a random UUID.
+
+ """
+ f = open("/proc/sys/kernel/random/uuid", "r")
+ try:
+ return f.read(128).rstrip("\n")
+ finally:
+ f.close()
+
+
+def WriteFile(file_name, fn=None, data=None,
+ mode=None, uid=-1, gid=-1,
+ atime=None, mtime=None):
+ """(Over)write a file atomically.
+
+ The file_name and either fn (a function taking one argument, the
+ file descriptor, and which should write the data to it) or data (the
+ contents of the file) must be passed. The other arguments are
+ optional and allow setting the file mode, owner and group, and the
+ mtime/atime of the file.
+
+ If the function doesn't raise an exception, it has succeeded and the
+ target file has the new contents. If the file has raised an
+ exception, an existing target file should be unmodified and the
+ temporary file should be removed.
+
+ """
+ if not os.path.isabs(file_name):
+ raise errors.ProgrammerError("Path passed to WriteFile is not"
+ " absolute: '%s'" % file_name)
+
+ if [fn, data].count(None) != 1:
+ raise errors.ProgrammerError("fn or data required")
+
+ if [atime, mtime].count(None) == 1:
+ raise errors.ProgrammerError("Both atime and mtime must be either"
+ " set or None")
+
+
+ dir_name, base_name = os.path.split(file_name)
+ fd, new_name = tempfile.mkstemp('.new', base_name, dir_name)
+ # here we need to make sure we remove the temp file, if any error
+ # leaves it in place
+ try:
+ if uid != -1 or gid != -1:
+ os.chown(new_name, uid, gid)
+ if mode:
+ os.chmod(new_name, mode)
+ if data is not None:
+ os.write(fd, data)
+ else:
+ fn(fd)
+ os.fsync(fd)
+ if atime is not None and mtime is not None:
+ os.utime(new_name, (atime, mtime))
+ os.rename(new_name, file_name)
+ finally:
+ os.close(fd)
+ RemoveFile(new_name)
+
+
+def all(seq, pred=bool):
+ "Returns True if pred(x) is True for every element in the iterable"
+ for elem in itertools.ifilterfalse(pred, seq):
+ return False
+ return True
+
+
+def any(seq, pred=bool):
+ "Returns True if pred(x) is True for at least one element in the iterable"
+ for elem in itertools.ifilter(pred, seq):
+ return True
+ return False