#!/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, cli_option("-n", "--node", dest="node", help="Target node and optional secondary node", metavar="[:]", completion_suggest=OPT_COMPL_INST_ADD_NODES), cli_option("-B", "--backend", dest="beparams", type="keyval", default={}, help="Backend parameters"), cli_option("-t", "--disk-template", dest="disk_template", help="Custom disk setup (diskless, file, plain, drbd)", default=None, metavar="TEMPL", choices=list(constants.DISK_TEMPLATES)), cli_option("--disk", help="Disk information", default=[], dest="disks", action="append", type="identkeyval"), cli_option("-s", "--os-size", dest="sd_size", help="Disk size for a" " single-disk configuration, when not using the --disk option," " in MiB unless a suffix is used", default=None, type="unit", metavar=""), cli_option("--net", help="NIC information", default=[], dest="nics", action="append", type="identkeyval"), cli_option("--no-nics", default=False, action="store_true", help="Do not create any network cards for the instance"), cli_option("--no-wait-for-sync", dest="wait_for_sync", default=True, action="store_false", help="Don't wait for sync (DANGEROUS!)"), 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=""), cli_option("--no-ip-check", dest="ip_check", default=True, action="store_false", help="Don't check that the instance's IP" " is alive"), cli_option("-I", "--iallocator", metavar="", help="Select nodes for the instance automatically using the" " iallocator plugin", default=None, type="string", completion_suggest=OPT_COMPL_ONE_IALLOCATOR), cli_option("--file-storage-dir", dest="file_storage_dir", help="Relative path under default cluster-wide file storage dir" " to store file-based disks", default=None, metavar=""), cli_option("--file-driver", dest="file_driver", help="Driver to use" " for image files", default="loop", metavar="", choices=list(constants.FILE_DRIVER)), cli_option("-H", "--hypervisor", dest="hypervisor", help="Hypervisor and hypervisor options, in the format" " hypervisor:option=value,option=value,...", default=None, type="identkeyval"), ] commands = { 'list': (PrintExportList, ARGS_NONE, [DEBUG_OPT, cli_option("--node", dest="nodes", default=[], action="append", help="List only backups stored on this node" " (can be used multiple times)", completion_suggest=OPT_COMPL_ONE_NODE), ], "", "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))