#!/usr/bin/python # # Copyright (C) 2006, 2007 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA # 02110-1301, USA. # pylint: disable-msg=W0401,W0614 # W0401: Wildcard import ganeti.cli # W0614: Unused import %s from wildcard import (since we need cli) import sys from ganeti.cli import * from ganeti import opcodes from ganeti import constants from ganeti import errors from ganeti import utils _VALUE_TRUE = "true" def PrintExportList(opts, args): """Prints a list of all the exported system images. @param opts: the command line options selected by the user @type args: list @param args: should be an empty list @rtype: int @return: the desired exit code """ exports = GetClient().QueryExports(opts.nodes, False) retcode = 0 for node in exports: ToStdout("Node: %s", node) ToStdout("Exports:") if isinstance(exports[node], list): for instance_name in exports[node]: ToStdout("\t%s", instance_name) else: ToStdout(" Could not get exports list") retcode = 1 return retcode def ExportInstance(opts, args): """Export an instance to an image in the cluster. @param opts: the command line options selected by the user @type args: list @param args: should contain only one element, the name of the instance to be exported @rtype: int @return: the desired exit code """ op = opcodes.OpExportInstance(instance_name=args[0], target_node=opts.node, shutdown=opts.shutdown) fin_resu, dlist = SubmitOpCode(op) if not isinstance(dlist, list): ToStderr("Cannot parse execution results") return 1 tot_dsk = len(dlist) # TODO: handle diskless instances if dlist.count(False) == 0: # all OK rcode = 0 elif dlist.count(True) == 0: ToStderr("Error: No disks were backed up successfully." " The export doesn't have any valid data," " it is recommended to retry the operation.") rcode = 1 else: ToStderr("Partial export failure: %d disks backed up, %d disks failed.", dlist.count(True), dlist.count(False)) rcode = 2 if not fin_resu: rcode = 1 return rcode def ImportInstance(opts, args): """Add an instance to the cluster. @param opts: the command line options selected by the user @type args: list @param args: should contain only one element, the new instance name @rtype: int @return: the desired exit code """ instance = args[0] (pnode, snode) = SplitNodeOption(opts.node) hypervisor = None hvparams = {} if opts.hypervisor: hypervisor, hvparams = opts.hypervisor if opts.nics: try: nic_max = max(int(nidx[0])+1 for nidx in opts.nics) except ValueError, err: raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err)) nics = [{}] * nic_max for nidx, ndict in opts.nics: nidx = int(nidx) if not isinstance(ndict, dict): msg = "Invalid nic/%d value: expected dict, got %s" % (nidx, ndict) raise errors.OpPrereqError(msg) nics[nidx] = ndict elif opts.no_nics: # no nics nics = [] else: # default of one nic, all auto nics = [{}] if opts.disk_template == constants.DT_DISKLESS: if opts.disks or opts.sd_size is not None: raise errors.OpPrereqError("Diskless instance but disk" " information passed") disks = [] else: if not opts.disks and not opts.sd_size: raise errors.OpPrereqError("No disk information specified") if opts.disks and opts.sd_size is not None: raise errors.OpPrereqError("Please use either the '--disk' or" " '-s' option") if opts.sd_size is not None: opts.disks = [(0, {"size": opts.sd_size})] try: disk_max = max(int(didx[0])+1 for didx in opts.disks) except ValueError, err: raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err)) disks = [{}] * disk_max for didx, ddict in opts.disks: didx = int(didx) if not isinstance(ddict, dict): msg = "Invalid disk/%d value: expected dict, got %s" % (didx, ddict) raise errors.OpPrereqError(msg) elif "size" not in ddict: raise errors.OpPrereqError("Missing size for disk %d" % didx) try: ddict["size"] = utils.ParseUnit(ddict["size"]) except ValueError, err: raise errors.OpPrereqError("Invalid disk size for disk %d: %s" % (didx, err)) disks[didx] = ddict utils.ForceDictType(opts.beparams, constants.BES_PARAMETER_TYPES) utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES) op = opcodes.OpCreateInstance(instance_name=instance, disk_template=opts.disk_template, disks=disks, nics=nics, mode=constants.INSTANCE_IMPORT, pnode=pnode, snode=snode, ip_check=opts.ip_check, start=False, src_node=opts.src_node, src_path=opts.src_dir, wait_for_sync=opts.wait_for_sync, file_storage_dir=opts.file_storage_dir, file_driver=opts.file_driver, iallocator=opts.iallocator, hypervisor=hypervisor, hvparams=hvparams, beparams=opts.beparams) SubmitOpCode(op) return 0 def RemoveExport(opts, args): """Remove an export from the cluster. @param opts: the command line options selected by the user @type args: list @param args: should contain only one element, the name of the instance whose backup should be removed @rtype: int @return: the desired exit code """ instance = args[0] op = opcodes.OpRemoveExport(instance_name=args[0]) SubmitOpCode(op) return 0 # this is defined separately due to readability only import_opts = [ DEBUG_OPT, NODE_PLACEMENT_OPT, BACKEND_OPT, DISK_TEMPLATE_OPT, DISK_OPT, OS_SIZE_OPT, NET_OPT, NONICS_OPT, NWSYNC_OPT, cli_option("--src-node", dest="src_node", help="Source node", metavar="", completion_suggest=OPT_COMPL_ONE_NODE), cli_option("--src-dir", dest="src_dir", help="Source directory", metavar=""), NOIPCHECK_OPT, IALLOCATOR_OPT, FILESTORE_DIR_OPT, FILESTORE_DRIVER_OPT, HYPERVISOR_OPT, ] commands = { 'list': (PrintExportList, ARGS_NONE, [DEBUG_OPT, NODE_LIST_OPT], "", "Lists instance exports available in the ganeti cluster"), 'export': (ExportInstance, ARGS_ONE_INSTANCE, [DEBUG_OPT, FORCE_OPT, cli_option("-n", "--node", dest="node", help="Target node", metavar="", completion_suggest=OPT_COMPL_ONE_NODE), cli_option("","--noshutdown", dest="shutdown", action="store_false", default=True, help="Don't shutdown the instance (unsafe)"), ], "-n [opts...] ", "Exports an instance to an image"), 'import': (ImportInstance, ARGS_ONE_INSTANCE, import_opts, ("[...] -t disk-type -n node[:secondary-node]" " "), "Imports an instance from an exported image"), 'remove': (RemoveExport, [ArgUnknown(min=1, max=1)], [DEBUG_OPT], "", "Remove exports of named instance from the filesystem."), } if __name__ == '__main__': sys.exit(GenericMain(commands))