"""Ganeti small utilities
+
"""
import shutil
import errno
import pwd
+import itertools
+import select
+import fcntl
+
+from cStringIO import StringIO
from ganeti import logger
from ganeti import errors
+from ganeti import constants
_locksheld = []
_re_shell_unquoted = re.compile('^[-.,=:/_+@A-Za-z0-9]+$')
+debug = False
+
class RunResult(object):
"""Simple class for holding the result of running external programs.
else:
self.fail_reason = "unable to determine termination reason"
+ if debug and self.failed:
+ logger.Debug("Command '%s' failed (%s); output: %s" %
+ (self.cmd, self.fail_reason, self.output))
+
def _GetOutput(self):
"""Returns the combined stdout and stderr for easier usage.
def _GetLockFile(subsystem):
"""Compute the file name for a given lock name."""
- return "/var/lock/ganeti_lock_%s" % subsystem
+ return "%s/ganeti_lock_%s" % (constants.LOCK_DIR, subsystem)
def Lock(name, max_retries=None, debug=False):
shell = True
env = os.environ.copy()
env["LC_ALL"] = "C"
+ poller = select.poll()
child = subprocess.Popen(cmd, shell=shell,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,
close_fds=True, env=env)
child.stdin.close()
- out = child.stdout.read()
- err = child.stderr.read()
+ poller.register(child.stdout, select.POLLIN)
+ poller.register(child.stderr, select.POLLIN)
+ out = StringIO()
+ err = StringIO()
+ fdmap = {
+ child.stdout.fileno(): (out, child.stdout),
+ child.stderr.fileno(): (err, child.stderr),
+ }
+ for fd in fdmap:
+ status = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, status | os.O_NONBLOCK)
+
+ while fdmap:
+ for fd, event in poller.poll():
+ if event & select.POLLIN or event & select.POLLPRI:
+ data = fdmap[fd][1].read()
+ # no data from read signifies EOF (the same as POLLHUP)
+ if not data:
+ poller.unregister(fd)
+ del fdmap[fd]
+ continue
+ fdmap[fd][0].write(data)
+ if (event & select.POLLNVAL or event & select.POLLHUP or
+ event & select.POLLERR):
+ poller.unregister(fd)
+ del fdmap[fd]
+
+ out = out.getvalue()
+ err = err.getvalue()
status = child.wait()
if status >= 0:
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.
+
+ """
+ # Ensure aliases are unique
+ aliases = UniqueSequence([hostname] + aliases)[1:]
+
+ 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:
+ 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.flush()
- os.rename(tmpname, file_name)
+ 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:
- f.close()
- finally:
- out.close()
+ 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:
+ 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.fsync(out)
+ os.rename(tmpname, file_name)
+ finally:
+ f.close()
+ finally:
+ 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)
"""Returns a list of all visible files in a directory.
"""
- return [i for i in os.listdir(path) if not i.startswith(".")]
+ files = [i for i in os.listdir(path) if not i.startswith(".")]
+ files.sort()
+ return files
def GetHomeDir(user, default=None):
return result.pw_dir
-def GetUUID():
+def NewUUID():
"""Returns a random UUID.
"""
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
+
+
+def UniqueSequence(seq):
+ """Returns a list with unique elements.
+
+ Element order is preserved.
+ """
+ seen = set()
+ return [i for i in seq if i not in seen and not seen.add(i)]
+
+
+def IsValidMac(mac):
+ """Predicate to check if a MAC address is valid.
+
+ Checks wether the supplied MAC address is formally correct, only
+ accepts colon separated format.
+ """
+ mac_check = re.compile("^([0-9a-f]{2}(:|$)){6}$")
+ return mac_check.match(mac) is not None
+
+
+def TestDelay(duration):
+ """Sleep for a fixed amount of time.
+
+ """
+ if duration < 0:
+ return False
+ time.sleep(duration)
+ return True