X-Git-Url: https://code.grnet.gr/git/ganeti-local/blobdiff_plain/ab92578a60fd9dd0ecaa802f76981d7160da17cd..919852dab960fd8c9e091f4d6500c0b246c663fc:/lib/client/gnt_instance.py diff --git a/lib/client/gnt_instance.py b/lib/client/gnt_instance.py index 21db801..d26efe7 100644 --- a/lib/client/gnt_instance.py +++ b/lib/client/gnt_instance.py @@ -27,6 +27,7 @@ import itertools import simplejson +import logging from cStringIO import StringIO from ganeti.cli import * @@ -36,6 +37,8 @@ from ganeti import compat from ganeti import utils from ganeti import errors from ganeti import netutils +from ganeti import ssh +from ganeti import objects _SHUTDOWN_CLUSTER = "cluster" @@ -244,97 +247,30 @@ def ListInstances(opts, args): """ selected_fields = ParseFields(opts.output, _LIST_DEF_FIELDS) - output = GetClient().QueryInstances(args, selected_fields, opts.do_locking) - - if not opts.no_headers: - headers = { - "name": "Instance", "os": "OS", "pnode": "Primary_node", - "snodes": "Secondary_Nodes", "admin_state": "Autostart", - "oper_state": "Running", - "oper_ram": "Memory", "disk_template": "Disk_template", - "oper_vcpus": "VCPUs", - "ip": "IP_address", "mac": "MAC_address", - "nic_mode": "NIC_Mode", "nic_link": "NIC_Link", - "bridge": "Bridge", - "sda_size": "Disk/0", "sdb_size": "Disk/1", - "disk_usage": "DiskUsage", - "status": "Status", "tags": "Tags", - "network_port": "Network_port", - "hv/kernel_path": "Kernel_path", - "hv/initrd_path": "Initrd_path", - "hv/boot_order": "Boot_order", - "hv/acpi": "ACPI", - "hv/pae": "PAE", - "hv/cdrom_image_path": "CDROM_image_path", - "hv/nic_type": "NIC_type", - "hv/disk_type": "Disk_type", - "hv/vnc_bind_address": "VNC_bind_address", - "serial_no": "SerialNo", "hypervisor": "Hypervisor", - "hvparams": "Hypervisor_parameters", - "be/memory": "Configured_memory", - "be/vcpus": "VCPUs", - "vcpus": "VCPUs", - "be/auto_balance": "Auto_balance", - "disk.count": "Disks", "disk.sizes": "Disk_sizes", - "nic.count": "NICs", "nic.ips": "NIC_IPs", - "nic.modes": "NIC_modes", "nic.links": "NIC_links", - "nic.bridges": "NIC_bridges", "nic.macs": "NIC_MACs", - "ctime": "CTime", "mtime": "MTime", "uuid": "UUID", - } - else: - headers = None - - unitfields = ["be/memory", "oper_ram", "sd(a|b)_size", "disk\.size/.*"] - numfields = ["be/memory", "oper_ram", "sd(a|b)_size", "be/vcpus", - "serial_no", "(disk|nic)\.count", "disk\.size/.*"] - - list_type_fields = ("tags", "disk.sizes", "nic.macs", "nic.ips", - "nic.modes", "nic.links", "nic.bridges") - # change raw values to nicer strings - for row in output: - for idx, field in enumerate(selected_fields): - val = row[idx] - if field == "snodes": - val = ",".join(val) or "-" - elif field == "admin_state": - if val: - val = "yes" - else: - val = "no" - elif field == "oper_state": - if val is None: - val = "(node down)" - elif val: # True - val = "running" - else: - val = "stopped" - elif field == "oper_ram": - if val is None: - val = "(node down)" - elif field == "oper_vcpus": - if val is None: - val = "(node down)" - elif field == "sda_size" or field == "sdb_size": - if val is None: - val = "N/A" - elif field == "ctime" or field == "mtime": - val = utils.FormatTime(val) - elif field in list_type_fields: - val = ",".join(str(item) for item in val) - elif val is None: - val = "-" - if opts.roman_integers and isinstance(val, int): - val = compat.TryToRoman(val) - row[idx] = str(val) - - data = GenerateTable(separator=opts.separator, headers=headers, - fields=selected_fields, unitfields=unitfields, - numfields=numfields, data=output, units=opts.units) - - for line in data: - ToStdout(line) + fmtoverride = dict.fromkeys(["tags", "disk.sizes", "nic.macs", "nic.ips", + "nic.modes", "nic.links", "nic.bridges", + "snodes"], + (lambda value: ",".join(str(item) + for item in value), + False)) - return 0 + return GenericList(constants.QR_INSTANCE, selected_fields, args, opts.units, + opts.separator, not opts.no_headers, + format_override=fmtoverride) + + +def ListInstanceFields(opts, args): + """List instance fields. + + @param opts: the command line options selected by the user + @type args: list + @param args: fields to list, or empty for all + @rtype: int + @return: the desired exit code + + """ + return GenericListFields(constants.QR_INSTANCE, args, opts.separator, + not opts.no_headers) def AddInstance(opts, args): @@ -953,15 +889,66 @@ def ConnectToInstanceConsole(opts, args): instance_name = args[0] op = opcodes.OpConnectConsole(instance_name=instance_name) - cmd = SubmitOpCode(op, opts=opts) - if opts.show_command: - ToStdout("%s", utils.ShellQuoteArgs(cmd)) + cl = GetClient() + try: + cluster_name = cl.QueryConfigValues(["cluster_name"])[0] + console_data = SubmitOpCode(op, opts=opts, cl=cl) + finally: + # Ensure client connection is closed while external commands are run + cl.Close() + + del cl + + return _DoConsole(objects.InstanceConsole.FromDict(console_data), + opts.show_command, cluster_name) + + +def _DoConsole(console, show_command, cluster_name, feedback_fn=ToStdout, + _runcmd_fn=utils.RunCmd): + """Acts based on the result of L{opcodes.OpConnectConsole}. + + @type console: L{objects.InstanceConsole} + @param console: Console object + @type show_command: bool + @param show_command: Whether to just display commands + @type cluster_name: string + @param cluster_name: Cluster name as retrieved from master daemon + + """ + assert console.Validate() + + if console.kind == constants.CONS_MESSAGE: + feedback_fn(console.message) + elif console.kind == constants.CONS_VNC: + feedback_fn("Instance %s has VNC listening on %s:%s (display %s)," + " URL ", + console.instance, console.host, console.port, + console.display, console.host, console.port) + elif console.kind == constants.CONS_SSH: + # Convert to string if not already one + if isinstance(console.command, basestring): + cmd = console.command + else: + cmd = utils.ShellQuoteArgs(console.command) + + srun = ssh.SshRunner(cluster_name=cluster_name) + ssh_cmd = srun.BuildCmd(console.host, console.user, cmd, + batch=True, quiet=False, tty=True) + + if show_command: + feedback_fn(utils.ShellQuoteArgs(ssh_cmd)) + else: + result = _runcmd_fn(ssh_cmd, interactive=True) + if result.failed: + logging.error("Console command \"%s\" failed with reason '%s' and" + " output %r", result.cmd, result.fail_reason, + result.output) + raise errors.OpExecError("Connection to console of instance %s failed," + " please check cluster configuration" % + console.instance) else: - result = utils.RunCmd(cmd, interactive=True) - if result.failed: - raise errors.OpExecError("Console command \"%s\" failed: %s" % - (utils.ShellQuoteArgs(cmd), result.fail_reason)) + raise errors.GenericError("Unknown console type '%s'" % console.kind) return constants.EXIT_SUCCESS @@ -1420,21 +1407,18 @@ commands = { "Show information on the specified instance(s)"), 'list': ( ListInstances, ARGS_MANY_INSTANCES, - [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT, SYNC_OPT, ROMAN_OPT], + [NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "[...]", - "Lists the instances and their status. The available fields are" - " (see the man page for details): status, oper_state, oper_ram," - " oper_vcpus, name, os, pnode, snodes, admin_state, admin_ram," - " disk_template, ip, mac, nic_mode, nic_link, sda_size, sdb_size," - " vcpus, serial_no," - " nic.count, nic.mac/N, nic.ip/N, nic.mode/N, nic.link/N," - " nic.macs, nic.ips, nic.modes, nic.links," - " disk.count, disk.size/N, disk.sizes," - " hv/NAME, be/memory, be/vcpus, be/auto_balance," - " hypervisor." - " The default field" - " list is (in order): %s." % utils.CommaJoin(_LIST_DEF_FIELDS), + "Lists the instances and their status. The available fields can be shown" + " using the \"list-fields\" command (see the man page for details)." + " The default field list is (in order): %s." % + utils.CommaJoin(_LIST_DEF_FIELDS), ), + "list-fields": ( + ListInstanceFields, [ArgUnknown()], + [NOHDR_OPT, SEP_OPT], + "[fields...]", + "Lists all available fields for instances"), 'reinstall': ( ReinstallInstance, [ArgInstance()], [FORCE_OPT, OS_OPT, FORCE_VARIANT_OPT, m_force_multi, m_node_opt,