X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/c92b310aa3ec7a63b3acb0410f5b6a4fca7ec2de..b544cfe0c31aa6ee7602d2855824fc2c67b6ffbe:/lib/backend.py diff --git a/lib/backend.py b/lib/backend.py index 9df5dda..401710c 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -30,6 +30,7 @@ import stat import errno import re import subprocess +import random from ganeti import logger from ganeti import errors @@ -200,6 +201,7 @@ def VerifyNode(what): if 'nodelist' in what: result['nodelist'] = {} + random.shuffle(what['nodelist']) for node in what['nodelist']: success, message = _GetSshRunner().VerifyNodeHostname(node) if not success: @@ -962,28 +964,6 @@ def _ErrnoOrStr(err): return detail -def _OSSearch(name, search_path=None): - """Search for OSes with the given name in the search_path. - - Args: - name: The name of the OS to look for - search_path: List of dirs to search (defaults to constants.OS_SEARCH_PATH) - - Returns: - The base_dir the OS resides in - - """ - if search_path is None: - search_path = constants.OS_SEARCH_PATH - - for dir_name in search_path: - t_os_dir = os.path.sep.join([dir_name, name]) - if os.path.isdir(t_os_dir): - return dir_name - - return None - - def _OSOndiskVersion(name, os_dir): """Compute and return the API version of a given OS. @@ -1073,12 +1053,12 @@ def OSFromDisk(name, base_dir=None): """ if base_dir is None: - base_dir = _OSSearch(name) - - if base_dir is None: - raise errors.InvalidOS(name, None, "OS dir not found in search path") + os_dir = utils.FindFile(name, constants.OS_SEARCH_PATH, os.path.isdir) + if os_dir is None: + raise errors.InvalidOS(name, None, "OS dir not found in search path") + else: + os_dir = os.path.sep.join([base_dir, name]) - os_dir = os.path.sep.join([base_dir, name]) api_version = _OSOndiskVersion(name, os_dir) if api_version != constants.OS_API_VERSION: @@ -1237,6 +1217,8 @@ def FinalizeExport(instance, snap_disks): config.set(constants.INISECT_INS, 'memory', '%d' % instance.memory) config.set(constants.INISECT_INS, 'vcpus', '%d' % instance.vcpus) config.set(constants.INISECT_INS, 'disk_template', instance.disk_template) + + nic_count = 0 for nic_count, nic in enumerate(instance.nics): config.set(constants.INISECT_INS, 'nic%d_mac' % nic_count, '%s' % nic.mac) @@ -1245,6 +1227,7 @@ def FinalizeExport(instance, snap_disks): # TODO: redundant: on load can read nics until it doesn't exist config.set(constants.INISECT_INS, 'nic_count' , '%d' % nic_count) + disk_count = 0 for disk_count, disk in enumerate(snap_disks): config.set(constants.INISECT_INS, 'disk%d_ivname' % disk_count, ('%s' % disk.iv_name)) @@ -1417,6 +1400,130 @@ def RenameBlockDevices(devlist): return result +def _TransformFileStorageDir(file_storage_dir): + """Checks whether given file_storage_dir is valid. + + Checks wheter the given file_storage_dir is within the cluster-wide + default file_storage_dir stored in SimpleStore. Only paths under that + directory are allowed. + + Args: + file_storage_dir: string with path + + Returns: + normalized file_storage_dir (string) if valid, None otherwise + + """ + file_storage_dir = os.path.normpath(file_storage_dir) + base_file_storage_dir = ssconf.SimpleStore().GetFileStorageDir() + if (not os.path.commonprefix([file_storage_dir, base_file_storage_dir]) == + base_file_storage_dir): + logger.Error("file storage directory '%s' is not under base file" + " storage directory '%s'" % + (file_storage_dir, base_file_storage_dir)) + return None + return file_storage_dir + + +def CreateFileStorageDir(file_storage_dir): + """Create file storage directory. + + Args: + file_storage_dir: string containing the path + + Returns: + tuple with first element a boolean indicating wheter dir + creation was successful or not + + """ + file_storage_dir = _TransformFileStorageDir(file_storage_dir) + result = True, + if not file_storage_dir: + result = False, + else: + if os.path.exists(file_storage_dir): + if not os.path.isdir(file_storage_dir): + logger.Error("'%s' is not a directory" % file_storage_dir) + result = False, + else: + try: + os.makedirs(file_storage_dir, 0750) + except OSError, err: + logger.Error("Cannot create file storage directory '%s': %s" % + (file_storage_dir, err)) + result = False, + return result + + +def RemoveFileStorageDir(file_storage_dir): + """Remove file storage directory. + + Remove it only if it's empty. If not log an error and return. + + Args: + file_storage_dir: string containing the path + + Returns: + tuple with first element a boolean indicating wheter dir + removal was successful or not + + """ + file_storage_dir = _TransformFileStorageDir(file_storage_dir) + result = True, + if not file_storage_dir: + result = False, + else: + if os.path.exists(file_storage_dir): + if not os.path.isdir(file_storage_dir): + logger.Error("'%s' is not a directory" % file_storage_dir) + result = False, + # deletes dir only if empty, otherwise we want to return False + try: + os.rmdir(file_storage_dir) + except OSError, err: + logger.Error("Cannot remove file storage directory '%s': %s" % + (file_storage_dir, err)) + result = False, + return result + + +def RenameFileStorageDir(old_file_storage_dir, new_file_storage_dir): + """Rename the file storage directory. + + Args: + old_file_storage_dir: string containing the old path + new_file_storage_dir: string containing the new path + + Returns: + tuple with first element a boolean indicating wheter dir + rename was successful or not + + """ + old_file_storage_dir = _TransformFileStorageDir(old_file_storage_dir) + new_file_storage_dir = _TransformFileStorageDir(new_file_storage_dir) + result = True, + if not old_file_storage_dir or not new_file_storage_dir: + result = False, + else: + if not os.path.exists(new_file_storage_dir): + if os.path.isdir(old_file_storage_dir): + try: + os.rename(old_file_storage_dir, new_file_storage_dir) + except OSError, err: + logger.Error("Cannot rename '%s' to '%s': %s" + % (old_file_storage_dir, new_file_storage_dir, err)) + result = False, + else: + logger.Error("'%s' is not a directory" % old_file_storage_dir) + result = False, + else: + if os.path.exists(old_file_storage_dir): + logger.Error("Cannot rename '%s' to '%s'. Both locations exist." % + old_file_storage_dir, new_file_storage_dir) + result = False, + return result + + class HooksRunner(object): """Hook runner. @@ -1432,9 +1539,6 @@ class HooksRunner(object): Args: - hooks_base_dir: if not None, this overrides the constants.HOOKS_BASE_DIR (useful for unittests) - - logs_base_dir: if not None, this overrides the - constants.LOG_HOOKS_DIR (useful for unittests) - - logging: enable or disable logging of script output """ if hooks_base_dir is None: @@ -1446,7 +1550,6 @@ class HooksRunner(object): """Exec one hook script. Args: - - phase: the phase - script: the full path to the script - env: the environment with which to exec the script @@ -1528,6 +1631,42 @@ class HooksRunner(object): return rr +class IAllocatorRunner(object): + """IAllocator runner. + + This class is instantiated on the node side (ganeti-noded) and not on + the master side. + + """ + def Run(self, name, idata): + """Run an iallocator script. + + Return value: tuple of: + - run status (one of the IARUN_ constants) + - stdout + - stderr + - fail reason (as from utils.RunResult) + + """ + alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH, + os.path.isfile) + if alloc_script is None: + return (constants.IARUN_NOTFOUND, None, None, None) + + fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.") + try: + os.write(fd, idata) + os.close(fd) + result = utils.RunCmd([alloc_script, fin_name]) + if result.failed: + return (constants.IARUN_FAILURE, result.stdout, result.stderr, + result.fail_reason) + finally: + os.unlink(fin_name) + + return (constants.IARUN_SUCCESS, result.stdout, result.stderr, None) + + class DevCacheManager(object): """Simple class for managing a cache of block device information.