All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.kv.impl.admin.client.ShowCommand Maven / Gradle / Ivy

Go to download

NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.

The newest version!
/*-
 * Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This file was distributed by Oracle as part of a version of Oracle NoSQL
 * Database made available at:
 *
 * http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
 *
 * Please see the LICENSE file included in the top-level directory of the
 * appropriate version of Oracle NoSQL Database for a copy of the license and
 * additional information.
 */

package oracle.kv.impl.admin.client;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TimeZone;
import java.util.TreeMap;

import oracle.kv.KVVersion;
import oracle.kv.impl.admin.AdminStatus;
import oracle.kv.impl.admin.CommandJsonUtils;
import oracle.kv.impl.admin.CommandResult;
import oracle.kv.impl.admin.CommandResult.CommandFails;
import oracle.kv.impl.admin.CommandResult.CommandSucceeds;
import oracle.kv.impl.admin.CommandServiceAPI;
import oracle.kv.impl.admin.Snapshot;
import oracle.kv.impl.admin.criticalevent.CriticalEvent;
import oracle.kv.impl.admin.param.AdminParams;
import oracle.kv.impl.admin.param.ArbNodeParams;
import oracle.kv.impl.admin.param.Parameters;
import oracle.kv.impl.admin.param.RepNodeParams;
import oracle.kv.impl.admin.param.StorageNodeParams;
import oracle.kv.impl.admin.plan.Plan;
import oracle.kv.impl.admin.plan.StatusReport;
import oracle.kv.impl.api.avro.AvroDdl;
import oracle.kv.impl.api.avro.AvroDdl.SchemaSummary;
import oracle.kv.impl.api.avro.AvroSchemaMetadata;
import oracle.kv.impl.api.avro.AvroSchemaStatus;
import oracle.kv.impl.api.table.NameUtils;
import oracle.kv.impl.api.table.TableImpl;
import oracle.kv.impl.api.table.TableJsonUtils;
import oracle.kv.impl.api.table.TableMetadata;
import oracle.kv.impl.metadata.Metadata.MetadataType;
import oracle.kv.impl.monitor.views.PerfEvent;
import oracle.kv.impl.monitor.views.ServiceChange;
import oracle.kv.impl.param.LoadParameters;
import oracle.kv.impl.param.Parameter;
import oracle.kv.impl.param.ParameterMap;
import oracle.kv.impl.param.ParameterState;
import oracle.kv.impl.param.ParameterState.Info;
import oracle.kv.impl.security.metadata.KVStoreUser.UserDescription;
import oracle.kv.impl.sna.StorageNodeAgentAPI;
import oracle.kv.impl.sna.StorageNodeStatus;
import oracle.kv.impl.topo.AdminId;
import oracle.kv.impl.topo.ArbNodeId;
import oracle.kv.impl.topo.Datacenter;
import oracle.kv.impl.topo.DatacenterId;
import oracle.kv.impl.topo.RepNodeId;
import oracle.kv.impl.topo.ResourceId;
import oracle.kv.impl.topo.StorageNode;
import oracle.kv.impl.topo.StorageNodeId;
import oracle.kv.impl.topo.Topology;
import oracle.kv.impl.util.CommandParser;
import oracle.kv.impl.util.FormatUtils;
import oracle.kv.impl.util.JsonUtils;
import oracle.kv.impl.util.TopologyPrinter;
import oracle.kv.impl.util.TopologyPrinter.Filter;
import oracle.kv.impl.util.registry.RegistryUtils;
import oracle.kv.table.Index;
import oracle.kv.table.Table;
import oracle.kv.util.ErrorMessage;
import oracle.kv.util.shell.Shell;
import oracle.kv.util.shell.ShellArgumentException;
import oracle.kv.util.shell.ShellCommand;
import oracle.kv.util.shell.ShellCommandResult;
import oracle.kv.util.shell.ShellException;
import oracle.kv.util.shell.ShellUsageException;
import oracle.kv.util.shell.ShowCommandBase;

import com.sleepycat.je.rep.ReplicatedEnvironment.State;

import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;

/*
 * show and its subcommands
 */
public class ShowCommand extends ShowCommandBase {
    private static final List subs =
                                       Arrays.asList(new ShowParameters(),
                                                     new ShowAdmins(),
                                                     new ShowEvents(),
                                                     new ShowFaults(),
                                                     new ShowIndexes(),
                                                     new ShowPerf(),
                                                     new ShowPlans(),
                                                     new ShowPools(),
                                                     new ShowSchemas(),
                                                     new ShowSnapshots(),
                                                     new ShowTables(),
                                                     new ShowTopology(),
                                                     new ShowDatacenters(),
                                                     new ShowUpgradeOrder(),
                                                     new ShowUsers(),
                                                     new ShowZones(),
                                                     new ShowVersions());

    private static final String SHOW_COMMAND_NAME = "show";

    ShowCommand() {
        super(subs);
    }

    /*
     * ShowParameters
     */
    @POST
    private static final class ShowParameters extends SubCommand {

        private ShowParameters() {
            super("parameters", 4);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {
            return new ShowParameterExecutor() {

                @Override
                public String parameterMapResult(ParameterMap map,
                                                 boolean showHidden,
                                                 Info info)
                    throws ShellException {
                    return CommandUtils.formatParams(
                               map, showHidden, info);
                }

                @Override
                public String snParamResult(boolean showHidden,
                                            Info info,
                                            StorageNodeParams snp)
                   throws ShellException {
                   String result =
                       CommandUtils.formatParams(snp.getMap(), showHidden,
                                                 info);
                   final ParameterMap storageDirMap =
                       snp.getStorageDirMap();
                   if (storageDirMap != null && !storageDirMap.isEmpty()) {
                       result += "Storage directories:" + eol;
                       for (Parameter param : storageDirMap) {
                           result +=
                               Shell.makeWhiteSpace(4) +
                               "path=" + param.getName() +
                               Shell.makeWhiteSpace(1) +
                               "size=" + param.asString() +
                               eol;
                       }
                   }
                   final ParameterMap rnLogDirMap =
                       snp.getRNLogDirMap();
                   if (rnLogDirMap != null && !rnLogDirMap.isEmpty()) {
                       result += "RN Log directories:" + eol;
                       for (Parameter param : rnLogDirMap) {
                           result +=
                               Shell.makeWhiteSpace(4) +
                               "path=" + param.getName() +
                               eol;
                       }
                   }
                   final ParameterMap adminDirMap =
                       snp.getAdminDirMap();
                   if (adminDirMap != null && !adminDirMap.isEmpty()) {
                        result += "Admin directory:" + eol;
                        for (Parameter param : adminDirMap) {
                            result +=
                                Shell.makeWhiteSpace(4) +
                                "path=" + param.getName() +
                                Shell.makeWhiteSpace(1) +
                                "size=" + param.asString() +
                                eol;
                        }
                    }
                   return result;
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowParameterExecutor
            implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                /*
                 * parameters -policy | -global | -security | -service 
                 */
                if (args.length < 2) {
                    shell.badArgCount(ShowParameters.this);
                }
                Shell.checkHelp(args, ShowParameters.this);
                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();
                String serviceName = null;
                boolean isPolicy = false;
                boolean isSecurity = false;
                boolean isGlobal = false;

                for (int i = 1; i < args.length; i++) {
                    final String arg = args[i];
                    if ("-policy".equals(arg)) {
                        isPolicy = true;
                    } else if ("-service".equals(arg)) {
                        serviceName =
                            Shell.nextArg(args, i++, ShowParameters.this);
                    } else if ("-security".equals(arg)) {
                        isSecurity = true;
                    } else if ("-global".equals(arg)) {
                        isGlobal = true;
                    } else {
                        shell.unknownArgument(arg, ShowParameters.this);
                    }
                }

                if (((isPolicy ? 1 : 0) +
                     (isSecurity ? 1 : 0) +
                     (isGlobal ? 1 : 0)) > 1) {
                    throw new ShellUsageException(
                        "-policy, -global and -security cannot be used " +
                        "together",
                        ShowParameters.this);
                }
                if (isPolicy) {
                    if (serviceName != null) {
                        throw new ShellUsageException(
                            "-policy cannot be combined with a service",
                            ShowParameters.this);
                    }
                    try {
                        final ParameterMap map = cs.getPolicyParameters();
                        return parameterMapResult(
                            map, cmd.getHidden(), ParameterState.Info.POLICY);
                    } catch (RemoteException re) {
                        cmd.noAdmin(re);
                    }
                }
                if (isSecurity) {
                    if (serviceName != null) {
                        throw new ShellUsageException
                            ("-security cannot be combined with a service",
                             ShowParameters.this);
                    }
                    try {
                        final ParameterMap map =
                            cs.getParameters().getGlobalParams().
                                getGlobalSecurityPolicies();
                        return parameterMapResult(map, cmd.getHidden(), null);
                    } catch (RemoteException re) {
                        cmd.noAdmin(re);
                    }
                }
                if (isGlobal) {
                    if (serviceName != null) {
                        throw new ShellUsageException(
                            "-global cannot be combined with a service",
                            ShowParameters.this);
                    }
                    try {
                        final ParameterMap map =
                            cs.getParameters().getGlobalParams().
                                getGlobalComponentsPolicies();
                        return parameterMapResult(map, cmd.getHidden(), null);
                    } catch (RemoteException re) {
                        cmd.noAdmin(re);
                    }
                }

                if (serviceName == null) {
                    shell.requiredArg("-service|-policy|-security|-global",
                                      ShowParameters.this);
                }

                RepNodeId rnid = null;
                AdminId aid = null;
                ArbNodeId anid = null;
                StorageNodeId snid = null;
                try {
                    rnid = RepNodeId.parse(serviceName);
                } catch (IllegalArgumentException ignored) {
                    try {
                        snid = StorageNodeId.parse(serviceName);
                    } catch (IllegalArgumentException ignored1) {
                        try {
                            aid = AdminId.parse(serviceName);
                        } catch (IllegalArgumentException ignored2) {
                            try {
                                anid = ArbNodeId.parse(serviceName);
                            } catch (IllegalArgumentException ignored3) {
                                invalidArgument(serviceName);
                            }
                        }
                    }
                }

                Parameters p;
                try {
                    p = cs.getParameters();
                    if (rnid != null) {
                        final RepNodeParams rnp = p.get(rnid);
                        if (rnp == null) {
                            noSuchService(rnid);
                        } else {
                            /*
                             * TODO : Need to check if we need to show
                             * rn log dir as part of show parameter
                             * -service rgx-rny
                             */
                            return parameterMapResult(
                                rnp.getMap(), cmd.getHidden(),
                                ParameterState.Info.REPNODE);
                        }
                    } else if (snid != null) {
                        final StorageNodeParams snp = p.get(snid);
                        if (snp == null) {
                            noSuchService(snid);
                        } else {
                            return snParamResult(cmd.getHidden(),
                                                 ParameterState.Info.SNA,
                                                 snp);
                        }
                    } else if (aid != null) {
                        final AdminParams ap = p.get(aid);
                        if (ap == null) {
                            noSuchService(aid);
                        } else {
                            return parameterMapResult(
                                ap.getMap(), cmd.getHidden(),
                                ParameterState.Info.ADMIN);
                        }
                    } else if (anid != null) {
                        final ArbNodeParams anp = p.get(anid);
                        if (anp == null) {
                            noSuchService(anid);
                        } else {
                            return parameterMapResult(
                                anp.getMap(), cmd.getHidden(),
                                ParameterState.Info.ARBNODE);
                        }
                    }
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }

            public abstract T
                parameterMapResult(ParameterMap map,
                                   boolean showHidden,
                                   ParameterState.Info info)
                throws ShellException;

            public abstract T snParamResult(boolean showHidden,
                                            ParameterState.Info info,
                                            StorageNodeParams snp)
                throws ShellException;
        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args,
                                                    Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show parameters");
            return new ShowParameterExecutor() {

                @Override
                public ShellCommandResult
                    parameterMapResult(ParameterMap map,
                                       boolean showHidden,
                                       Info info)
                    throws ShellException {
                    scr.setReturnValue(
                        CommandUtils.formatParamsJson(map, showHidden, info));
                    return scr;
                }

                @Override
                public ShellCommandResult snParamResult(boolean showHidden,
                                                        Info info,
                                                        StorageNodeParams snp)
                    throws ShellException {
                    final ObjectNode snNode =
                        CommandUtils.formatParamsJson(snp.getMap(),
                                                      showHidden, info);
                    final ParameterMap storageDirMap = snp.getStorageDirMap();
                    if (storageDirMap != null && !storageDirMap.isEmpty()) {
                        final ArrayNode storageDirArray =
                            snNode.putArray("storageDirs");
                        for (Parameter param : storageDirMap) {
                            final ObjectNode dirNode =
                                JsonUtils.createObjectNode();
                            dirNode.put("path", param.getName());
                            dirNode.put("size", param.asString());
                            storageDirArray.add(dirNode);
                        }
                    }
                    final ParameterMap rnLogDirMap = snp.getRNLogDirMap();
                    if (rnLogDirMap != null && !rnLogDirMap.isEmpty()) {
                        final ArrayNode rnLogDirArray =
                            snNode.putArray("rnlogDirs");
                        for (Parameter param : rnLogDirMap) {
                            final ObjectNode dirNode =
                                JsonUtils.createObjectNode();
                            dirNode.put("path", param.getName());
                            rnLogDirArray.add(dirNode);
                        }
                    }
                    final ParameterMap adminDirMap = snp.getAdminDirMap();
                    if (adminDirMap != null && !adminDirMap.isEmpty()) {
                        final ArrayNode adminDirArray =
                            snNode.putArray("adminDirs");
                        for (Parameter param : adminDirMap) {
                            final ObjectNode dirNode =
                                JsonUtils.createObjectNode();
                            dirNode.put("path", param.getName());
                            dirNode.put("size", param.asString());
                            adminDirArray.add(dirNode);
                        }
                    }
                    scr.setReturnValue(snNode);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return "show parameters -policy | -global | -security |" +
                   " -service  " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Displays service parameters and state for the specified " +
                "service." + eolt + "The service may be a RepNode, " +
                "StorageNode, or Admin service," + eolt + "as identified " +
                "by any valid string, for example" + eolt + "rg1-rn1, sn1, " +
                "admin2, etc.  Use the -policy flag to show global policy" +
                eolt + "default parameters. Use the -security flag to show global " +
                "security parameters. Use the -global flag to show global " +
                "component parameters.";
        }

        private void noSuchService(ResourceId rid)
            throws ShellException {

            throw new ShellArgumentException
                ("No such service: " + rid);
        }
    }

    /*
     * ShowAdmins
     */
    @POST
    private static final class ShowAdmins extends SubCommand {

        private ShowAdmins() {
            super("admins", 2);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {
            return new ShowAdminExecutor() {

                @Override
                public String adminsResult(List admins,
                                           Topology t,
                                           String currentAdminHost,
                                           int currentAdminPort,
                                           RegistryUtils registryUtils)
                    throws ShellException {
                    final StringBuilder sb = new StringBuilder();
                    sb.append("");
                    for (ParameterMap map : admins) {
                        final AdminParams params = new AdminParams(map);
                        final StorageNodeId snId =
                            params.getStorageNodeId();
                        sb.append(params.getAdminId());
                        sb.append(": Storage Node ").append(snId);
                        sb.append(" type=").append(params.getType());

                        sb.append(" (");
                        final StorageNode sn = t.get(snId);
                        if (currentAdminHost.equals(sn.getHostname()) &&
                            currentAdminPort == sn.getRegistryPort()) {
                            sb.append("connected ");
                        }
                        AdminStatus adminStatus;
                        try {
                            adminStatus =
                                registryUtils.getAdmin(snId).
                                getAdminStatus();
                        } catch (Exception e) {
                            adminStatus = null;
                        }
                        if (adminStatus == null) {
                            sb.append("UNREACHABLE");
                        } else {
                            sb.append(adminStatus.getServiceStatus());
                            final State state =
                                adminStatus.getReplicationState();
                            if (state != null) {
                                sb.append(",").append(state);
                                if (state.isMaster() &&
                                    !adminStatus.
                                        getIsAuthoritativeMaster()) {
                                    sb.append(" (non-authoritative)");
                                }
                            }
                        }
                        sb.append(")");
                        sb.append(eol);
                    }
                    return sb.toString();
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowAdminExecutor
            implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                Shell.checkHelp(args, ShowAdmins.this);
                if (args.length > 2) {
                    shell.badArgCount(ShowAdmins.this);
                }
                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();

                final String currentAdminHost = cmd.getAdminHostname();
                final int currentAdminPort = cmd.getAdminPort();

                try {
                    final List admins = cs.getAdmins();
                    final Topology t = cs.getTopology();
                    final RegistryUtils registryUtils = new RegistryUtils(t,
                        cmd.getLoginManager());
                    return adminsResult(admins, t, currentAdminHost,
                        currentAdminPort, registryUtils);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }

            public abstract T adminsResult(List admins,
                                           Topology t,
                                           String currentAdminHost,
                                           int currentAdminPort,
                                           RegistryUtils registryUtils)
                throws ShellException;
        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show admins");
            return new ShowAdminExecutor() {

                @Override
                public ShellCommandResult
                    adminsResult(List admins, Topology t,
                                 String currentAdminHost, int currentAdminPort,
                                 RegistryUtils registryUtils)
                    throws ShellException {

                    final ObjectNode top = JsonUtils.createObjectNode();
                    final ArrayNode adminArray = top.putArray("admins");
                    for (ParameterMap map : admins) {
                        final ObjectNode adminNode =
                            JsonUtils.createObjectNode();
                        final AdminParams params = new AdminParams(map);
                        final StorageNodeId snId = params.getStorageNodeId();
                        adminNode.put(
                            "adminId", params.getAdminId().toString());
                        adminNode.put("snId", snId.toString());
                        adminNode.put("type", params.getType().toString());

                        adminNode.put("connected", false);
                        final StorageNode sn = t.get(snId);
                        if (currentAdminHost != null &&
                            currentAdminHost.equals(sn.getHostname()) &&
                            currentAdminPort == sn.getRegistryPort()) {
                            adminNode.put("connected", true);
                        }

                        AdminStatus adminStatus;
                        try {
                            adminStatus =
                                registryUtils.getAdmin(snId).getAdminStatus();
                        } catch (Exception e) {
                            adminStatus = null;
                        }
                        if (adminStatus == null) {
                            adminNode.put("adminStatus", "UNREACHABLE");
                        } else {
                            adminNode.put("adminStatus",
                                          adminStatus.getServiceStatus().
                                              toString());
                            final State state =
                                adminStatus.getReplicationState();
                            if (state != null) {
                                adminNode.put(
                                    "replicationState", state.toString());
                                adminNode.put("authoritative", true);
                                if (state.isMaster() &&
                                    !adminStatus.getIsAuthoritativeMaster()) {
                                    adminNode.put("authoritative", false);
                                }
                            }
                        }
                        adminArray.add(adminNode);
                    }
                    scr.setReturnValue(top);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return "show admins " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return "Displays basic information about deployed Admin services.";
        }
    }

    /*
     * ShowSchemas
     */
    @POST
    static final class ShowSchemas extends SubCommand {

        private ShowSchemas() {
            super("schemas", 3);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            return new ShowSchemaExecutor() {

                @Override
                public String emptySchemaResult() {
                    return "";
                }

                @Override
                public String
                    multiSchemasResult(SortedMap map)
                    throws ShellException {
                    final StringBuilder builder =
                        new StringBuilder(map.size() * 100);

                    for (final AvroDdl.SchemaSummary summary :
                         map.values()) {
                        if (builder.length() > 0) {
                            builder.append(eol);
                        }
                        builder.append(summary.getName());
                        formatAvroSchemaSummary(summary, builder);
                        AvroDdl.SchemaSummary prevVersion =
                            summary.getPreviousVersion();
                        while (prevVersion != null) {
                            formatAvroSchemaSummary(prevVersion, builder);
                            prevVersion = prevVersion.getPreviousVersion();
                        }
                    }
                    return builder.toString();
                }

                @Override
                public String singleSchemaResult(String schema,
                                                 CommandServiceAPI cs,
                                                 CommandShell cmd)
                    throws ShellException {
                    return showSingleSchema(schema, cs, cmd);
                }

            }.commonExecute(args, shell);
        }

        private abstract class ShowSchemaExecutor implements Executor {

            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                Shell.checkHelp(args, ShowSchemas.this);
                if (args.length > 3) {
                    shell.badArgCount(ShowSchemas.this);
                }

                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();

                try {

                    if (args.length == 3) {
                        if ("-name".equals(args[1])) {
                            return singleSchemaResult(args[2], cs, cmd);
                        }
                        shell.unknownArgument(args[1], ShowSchemas.this);
                    }
                    final boolean includeDisabled;
                    if (args.length == 2) {
                        if (!"-disabled".equals(args[1])) {
                            shell.unknownArgument(args[1], ShowSchemas.this);
                        }
                        includeDisabled = true;
                    } else {
                        includeDisabled = false;
                    }

                    final SortedMap map =
                        cs.getSchemaSummaries(includeDisabled);

                    if (map.isEmpty()) {
                        return emptySchemaResult();
                    }

                    return multiSchemasResult(map);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }

            public abstract T emptySchemaResult();

            public abstract T
                singleSchemaResult(String schema,
                                   CommandServiceAPI cs,
                                   CommandShell cmd)
                throws ShellException, RemoteException;

            public abstract T
                multiSchemasResult(SortedMap map)
                throws ShellException;

        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show schemas");
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode schemaArray = top.putArray("schemas");
            return new ShowSchemaExecutor() {

                @Override
                public ShellCommandResult emptySchemaResult() {
                    scr.setDescription("No schema.");
                    return scr;
                }

                @Override
                public ShellCommandResult
                    singleSchemaResult(String schema,
                                       CommandServiceAPI cs,
                                       CommandShell cmd)
                    throws ShellException, RemoteException {
                    final ObjectNode on = JsonUtils.createObjectNode();
                    final int id =
                        parseSchemaNameAndId(schema, false /*idRequired*/,
                                             cs, ShowSchemas.this);
                    final AvroDdl.SchemaDetails details =
                        cs.getSchemaDetails(id);
                    on.put("schemaId", details.getId());
                    on.put(name, details.getName());
                    on.put("status",
                           details.getMetadata().getStatus().name());
                    on.put("modified", FormatUtils.formatDateAndTime(
                        details.getMetadata().getTimeModified()));
                    on.put("from", details.getMetadata().getFromMachine());
                    if (!details.getMetadata().getByUser().isEmpty()) {
                        on.put("by", details.getMetadata().getByUser());
                    }

                    schemaArray.add(on);
                    scr.setReturnValue(top);
                    return scr;
                }

                @Override
                public ShellCommandResult
                    multiSchemasResult(SortedMap map)
                    throws ShellException {

                    for (final AvroDdl.SchemaSummary summary :
                         map.values()) {
                        schemaArray.add(
                            formatAvroSchemaSummaryJson(summary));
                    }
                    scr.setReturnValue(top);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return "show schemas [-disabled] | [-name ] " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Displays schema details of the named schema or a list of " +
                "schemas" + eolt + "registered with the store. The " +
                "-disabled flag enables listing of" + eolt + "disabled " +
                "schemas.";
        }

        private void formatAvroSchemaSummary(AvroDdl.SchemaSummary summary,
                                             StringBuilder builder) {
            builder.append(eol).append("  ID: ").append(summary.getId());
            final AvroSchemaMetadata metadata = summary.getMetadata();
            if (metadata.getStatus() == AvroSchemaStatus.DISABLED) {
                builder.append(" (disabled)");
            }
            builder.append("  Modified: ").append
                (FormatUtils.formatDateAndTime(metadata.getTimeModified()));
            builder.append(", From: ").append(metadata.getFromMachine());
            if (!metadata.getByUser().isEmpty()) {
                builder.append(", By: ").append(metadata.getByUser());
            }
        }

        private ObjectNode
            formatAvroSchemaSummaryJson(AvroDdl.SchemaSummary summary) {
            final ObjectNode on = JsonUtils.createObjectNode();
            on.put("schemaId", summary.getId());
            on.put("name", summary.getName());
            on.put("status", summary.getMetadata().getStatus().name());
            on.put("modified", FormatUtils.formatDateAndTime(
                summary.getMetadata().getTimeModified()));
            on.put("from", summary.getMetadata().getFromMachine());
            if (!summary.getMetadata().getByUser().isEmpty()) {
                on.put("by", summary.getMetadata().getByUser());
            }
            final AvroDdl.SchemaSummary previous =
                summary.getPreviousVersion();
            if (previous != null) {
                on.put("previous", formatAvroSchemaSummaryJson(previous));
            }
            return on;
        }

        /**
         * Returns a valid schema ID for the given schemaName[.ID] string, or
         * throws ShellUsageException if there is no such schema.  The .ID
         * is required if the schema is disabled.
         *
         * @param idRequired is true if the .ID portion is reqiured.
         *
         * This is static so that it can be called from the DdlCommand as well.
         */
        protected static int parseSchemaNameAndId(String nameAndId,
                                                  boolean idRequired,
                                                  CommandServiceAPI cs,
                                                  ShellCommand command)
            throws ShellException, RemoteException {

            /* Get trailing .ID value, if any. */
            final int offset = nameAndId.lastIndexOf(".");
            int id = 0;
            if (offset > 0 && offset < nameAndId.length() - 1) {
                final String idArg = nameAndId.substring(offset + 1);
                try {
                    id = Integer.parseInt(idArg);
                    if (id <= 0) {
                        throw new ShellUsageException
                            ("Illegal schema ID: " + id, command);
                    }
                } catch (NumberFormatException e) /* CHECKSTYLE:OFF */ {
                    /* The ID remains zero. */
                } /* CHECKSTYLE:ON */
            }

            /* Get a map of all schemas. */
            final SortedMap map =
                cs.getSchemaSummaries(true /*includeDisabled*/);

            /*
             * If there is no trailing .ID, use the first active schema with
             * the given name.
             */
            if (id == 0) {
                if (idRequired) {
                    throw new ShellUsageException
                        (nameAndId + " does not containing a trailing .ID " +
                         "value", command);

                }

                final String name = nameAndId;
                AvroDdl.SchemaSummary summary = map.get(name);
                while (summary != null) {
                    if (summary.getMetadata().getStatus() !=
                        AvroSchemaStatus.DISABLED) {
                        return summary.getId();
                    }
                    summary = summary.getPreviousVersion();
                }
                throw new ShellUsageException
                    ("Schema " + name + " does not exist or is disabled." +
                     eol + "Use . to refer to a disabled " +
                     "schema.", command);
            }

            /*
             * Return the ID if there is such a schema with the given name and
             * ID.
             */
            final String name = nameAndId.substring(0, offset);
            AvroDdl.SchemaSummary summary = map.get(name);
            while (summary != null) {
                if (id == summary.getId()) {
                    return id;
                }
                summary = summary.getPreviousVersion();
            }
            throw new ShellUsageException
                ("Schema " + name + " does not exist or does not have ID " +
                 id, command);
        }

        private String showSingleSchema(String nameAndId,
                                        CommandServiceAPI cs,
                                        CommandShell cmd)
            throws ShellException {

            try {
                final int id;
                id = parseSchemaNameAndId(nameAndId, false /*idRequired*/,
                                          cs, this);
                final AvroDdl.SchemaDetails details = cs.getSchemaDetails(id);
                return details.getText();
            } catch (RemoteException re) {
                cmd.noAdmin(re);
            }
            return cantGetHere;
        }
    }

    /*
     * ShowTopology
     */
    @POST
    static final class ShowTopology extends SubCommand {
        static final String rnFlag = "-rn";
        static final String snFlag = "-sn";
        static final String stFlag = "-store";
        static final String shFlag = "-shard";
        static final String statusFlag = "-status";
        static final String perfFlag = "-perf";
        static final String anFlag = "-an";
        static final String dcFlagsDeprecation =
            "The -dc flag is deprecated and has been replaced by -zn." +
            eol + eol;

        private ShowTopology() {
            super("topology", 4);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            return new ShowTopologyExecutor() {
                @Override
                public String
                    createTopologyResult(Topology t, PrintStream out,
                                         Parameters p,
                                         EnumSet filter,
                                         Map
                                             statusMap,
                                         Map perfMap,
                                         boolean verbose,
                                         ByteArrayOutputStream outStream,
                                         String deprecatedDcFlagPrefix)
                    throws ShellException{
                    TopologyPrinter.printTopology(t, out, p, filter,
                                                  statusMap,
                                                  perfMap,
                                                  verbose);
                    return deprecatedDcFlagPrefix + outStream;
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowTopologyExecutor implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                EnumSet filter =
                    EnumSet.noneOf(Filter.class);
                Shell.checkHelp(args, ShowTopology.this);
                boolean hasComponents = false;
                boolean deprecatedDcFlag = false;
                if (args.length > 1) {
                    for (int i = 1; i < args.length; i++) {
                        if (CommandUtils.isDatacenterIdFlag(args[i])) {
                            filter.add(Filter.DC);
                            hasComponents = true;
                            if ("-dc".equals(args[i])) {
                                deprecatedDcFlag = true;
                            }
                        } else if (args[i].equals(rnFlag)) {
                            filter.add(Filter.RN);
                            hasComponents = true;
                        } else if (args[i].equals(snFlag)) {
                            filter.add(Filter.SN);
                            hasComponents = true;
                        } else if (args[i].equals(stFlag)) {
                            filter.add(Filter.STORE);
                            hasComponents = true;
                        } else if (args[i].equals(shFlag)) {
                            filter.add(Filter.SHARD);
                            hasComponents = true;
                        } else if (args[i].equals(statusFlag)) {
                            filter.add(Filter.STATUS);
                        } else if (args[i].equals(perfFlag)) {
                            filter.add(Filter.PERF);
                        } else if (args[i].equals(anFlag)) {
                            filter.add(Filter.AN);
                            hasComponents = true;
                        } else {
                            shell.unknownArgument(args[i], ShowTopology.this);
                        }
                    }
                } else {
                    filter = TopologyPrinter.all;
                    hasComponents = true;
                }

                if (!hasComponents) {
                    filter.addAll(TopologyPrinter.components);
                }

                final String deprecatedDcFlagPrefix =
                    !deprecatedDcFlag ? "" : dcFlagsDeprecation;

                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();
                try {
                    final ByteArrayOutputStream outStream =
                        new ByteArrayOutputStream();
                    final PrintStream out = new PrintStream(outStream);

                    final Topology t = cs.getTopology();
                    final Parameters p = cs.getParameters();
                    Map statusMap = null;
                    if (filter.contains(Filter.STATUS)) {
                        statusMap = cs.getStatusMap();
                    }
                    Map perfMap = null;
                    if (filter.contains(Filter.PERF)) {
                        perfMap = cs.getPerfMap();
                    }
                    return createTopologyResult(t, out, p, filter, statusMap,
                                                perfMap, shell.getVerbose(),
                                                outStream,
                                                deprecatedDcFlagPrefix);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }

            public abstract T
                createTopologyResult(Topology t, PrintStream out,
                                     Parameters params,
                                     EnumSet filter,
                                     Map statusMap,
                                     Map perfMap,
                                     boolean verbose,
                                     ByteArrayOutputStream outStream,
                                     String deprecatedDcFlagPrefix)
                throws ShellException;
        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show topology");
            return new ShowTopologyExecutor() {
                @Override
                public ShellCommandResult
                    createTopologyResult(Topology t, PrintStream out,
                                         Parameters p,
                                         EnumSet filter,
                                         Map
                                             statusMap,
                                         Map perfMap,
                                         boolean verbose,
                                         ByteArrayOutputStream outStream,
                                         String deprecatedDcFlagPrefix)
                    throws ShellException{
                    scr.setReturnValue(
                        TopologyPrinter.printTopologyJson(
                            t, p, filter, verbose));
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return
                "show topology [-zn] [-rn] [-an] [-sn] [-store] [-status]" +
                " [-perf] " +
                CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Displays the current, deployed topology. " +
                "By default show the entire " + eolt +
                "topology. The optional flags restrict the " +
                "display to one or more of" + eolt + "Zones, " +
                "RepNodes, StorageNodes and Storename," + eolt + "or " +
                "specify service status or performance.";
        }
    }

    /*
     * ShowEvents
     */
    @POST
    private static final class ShowEvents extends SubCommand {

        private ShowEvents() {
            super("events", 2);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            return new ShowEventExecutor() {

                @Override
                public String singleEvent(String[] shellArgs,
                                          CommandServiceAPI cs,
                                          CommandShell cmd,
                                          Shell commandShell)
                    throws ShellException {
                    return showSingleEvent(shellArgs, cs, cmd, commandShell);
                }

                @Override
                public String multiEvents(List events)
                    throws ShellException {
                    String msg = "";
                    for (CriticalEvent ev : events) {
                        msg += ev.toString() + eol;
                    }
                    return msg;
                }

            }.commonExecute(args, shell);
        }

        private abstract class ShowEventExecutor
            implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                Shell.checkHelp(args, ShowEvents.this);
                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();
                if (Shell.checkArg(args, "-id")) {
                    return singleEvent(args, cs, cmd, shell);
                }
                try {
                    Date fromTime = null;
                    Date toTime = null;
                    CriticalEvent.EventType et = CriticalEvent.EventType.ALL;

                    for (int i = 1; i < args.length; i++) {
                        if ("-from".equals(args[i])) {
                            if (++i >= args.length) {
                                shell.badArgCount(ShowEvents.this);
                            }
                            fromTime =
                                parseTimestamp(args[i], ShowEvents.this);
                            if (fromTime == null) {
                                throw new ShellArgumentException(
                                    "Can't parse " + args[i] +
                                    " as a timestamp.");
                            }
                        } else if ("-to".equals(args[i])) {
                            if (++i >= args.length) {
                                shell.badArgCount(ShowEvents.this);
                            }
                            toTime = parseTimestamp(args[i], ShowEvents.this);
                        } else if ("-type".equals(args[i])) {
                            if (++i >= args.length) {
                                shell.badArgCount(ShowEvents.this);
                            }
                            try {
                                final String etype = args[i].toUpperCase();
                                et = Enum.valueOf(
                                         CriticalEvent.EventType.class,
                                         etype);
                            } catch (IllegalArgumentException iae) {
                                throw new ShellUsageException
                                    ("Can't parse " + args[i] +
                                     " as an EventType.", ShowEvents.this);
                            }
                        } else {
                            shell.unknownArgument(args[i], ShowEvents.this);
                        }
                    }

                    final long from =
                        (fromTime == null ? 0L : fromTime.getTime());
                    final long to = (toTime == null ? 0L : toTime.getTime());

                    final List events =
                        cs.getEvents(from, to, et);
                    return multiEvents(events);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }

            public abstract T
                singleEvent(String[] args, CommandServiceAPI cs,
                            CommandShell cmd, Shell shell)
                throws ShellException;

            public abstract T multiEvents(List events)
                throws ShellException;
        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args,
                                                    Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show events");
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode eventArray = top.putArray("events");
            return new ShowEventExecutor() {

                @Override
                public ShellCommandResult singleEvent(String[] shellArgs,
                                                      CommandServiceAPI cs,
                                                      CommandShell cmd,
                                                      Shell commandShell)
                    throws ShellException {

                    if (args.length != 3) {
                        shell.badArgCount(ShowEvents.this);
                    }

                    if (!"-id".equals(args[1])) {
                        shell.unknownArgument(args[1], ShowEvents.this);
                    }
                    final String eventId = args[2];
                    try {
                        final CriticalEvent event =
                            cs.getOneEvent(eventId);
                        if (event == null) {
                            throw new ShellArgumentException(
                                "No event matches the id " + eventId);
                        }
                        eventArray.add(event.getDetailString());
                        scr.setReturnValue(top);
                        return scr;
                    } catch (RemoteException re) {
                        cmd.noAdmin(re);
                    }
                    return null;
                }

                @Override
                public ShellCommandResult
                    multiEvents(List events)
                    throws ShellException {
                    for (CriticalEvent ev : events) {
                        eventArray.add(ev.getDetailString());
                    }
                    scr.setReturnValue(top);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return "show events [-id ] | [-from ] " +
                "[-to ]" + eolt + "[-type ] " +
                CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Displays event details or list of store events.  Status " +
                "events indicate" + eolt + "changes in service status.  " +
                "Log events correspond to records written " + eolt + "to " +
                "the store's log, except that only records logged at " +
                "\"SEVERE\" are " + eolt + "displayed; which should be " +
                "investigated immediately.  To view records " + eolt +
                "logged at \"WARNING\" or lower consult the store's log " +
                "file." + eolt + "Performance events are not usually " +
                "critical but may merit investigation." + eolt + eolt +
                getDateFormatsUsage();
        }

        private String showSingleEvent(String[] args,
                                       CommandServiceAPI cs,
                                       CommandShell cmd,
                                       Shell shell)
            throws ShellException {

            if (args.length != 3) {
                shell.badArgCount(this);
            }

            if (!"-id".equals(args[1])) {
                shell.unknownArgument(args[1], this);
            }
            final String eventId = args[2];
            try {
                final CriticalEvent event = cs.getOneEvent(eventId);
                if (event == null) {
                    return "No event matches the id " + eventId;
                }
                return event.getDetailString();
            } catch (RemoteException re) {
                cmd.noAdmin(re);
            }
            return cantGetHere;
        }
    }

    /*
     * ShowPlans
     *
     * show plans with no arguments: list the ten most recent plans.
     *
     * -last: show details of the most recently created plan.
     *
     * -id : show details of the plan with the given sequence number, or
     *   If -num  is also given, list  plans, starting with plan #.
     *
     * -num : set the number of plans to list. Defaults to 10.
     *   If unaccompanied: list the n most recently created plans.
     *
     * -from : list plans starting with those created after .
     *
     * -to : list plans ending with those created before .
     *   Combining -from with -to describes the range between the two .
     *   Otherwise -num  applies; its absence implies the default of 10.
     */
    @POST
    private static final class ShowPlans extends SubCommandJsonConvert {

        private static final String lastPlanNotFound =
            "Found no plans created by the current user.";

        private String operation;

        private ShowPlans() {
            super("plans", 2);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            Shell.checkHelp(args, this);
            final CommandShell cmd = (CommandShell) shell;
            final CommandServiceAPI cs = cmd.getAdmin();

            operation = SHOW_COMMAND_NAME + " " + getCommandName();
            if ((args.length == 3 && Shell.checkArg(args, "-id")) ||
                 (args.length == 2 && Shell.checkArg(args, "-last"))) {
                return showSinglePlan(shell, args, cs, cmd);
            }

            int planId = 0;
            int howMany = 0;
            Date fromTime = null, toTime = null;

            for (int i = 1; i < args.length; i++) {
                final String arg = args[i];
                if ("-id".equals(arg)) {
                    planId = parseUnsignedInt(Shell.nextArg(args, i++, this));
                } else if ("-num".equals(arg)) {
                    howMany = parseUnsignedInt(Shell.nextArg(args, i++, this));
                } else if ("-from".equals(arg)) {
                    fromTime =
                        parseTimestamp(Shell.nextArg(args, i++, this), this);
                } else if ("-to".equals(arg)) {
                    toTime =
                        parseTimestamp(Shell.nextArg(args, i++, this), this);
                } else {
                    shell.unknownArgument(arg, this);
                }
            }

            if (planId != 0 && !(fromTime == null && toTime == null)) {
                throw new ShellUsageException
                    ("-id cannot be used in combination with -from or -to",
                     this);
            }

            /* If no other range selector is given, default to most recent. */
            if (planId == 0 && fromTime == null && toTime == null) {
                toTime = new Date();
            }

            /* If no other range limit is given, default to 10. */
            if ((fromTime == null || toTime == null) && howMany == 0) {
                howMany = 10;
            }

            /*
             * If a time-based range is requested, we need to get plan ID range
             * information first.
             */
            if (! (fromTime == null && toTime == null)) {
                try {

                    int range[] =
                        cs.getPlanIdRange
                        (fromTime == null ? 0L : fromTime.getTime(),
                         toTime == null ? 0L : toTime.getTime(),
                         howMany);

                    planId = range[0];
                    howMany = range[1];
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
            }

            String msg = "";
            ObjectNode returnNode = JsonUtils.createObjectNode();
            ArrayNode planNodes = returnNode.putArray("plans");
            while (howMany > 0) {
                try {
                    SortedMap sortedPlans =
                        new TreeMap
                        (cs.getPlanRange(planId, howMany));

                    /* If we got zero plans back, we're out of plans. */
                    if (sortedPlans.size() == 0) {
                        break;
                    }

                    for (Integer k : sortedPlans.keySet()) {
                        final Plan p = sortedPlans.get(k);
                        if (shell.getJson()) {
                            ObjectNode node = JsonUtils.createObjectNode();
                            node.put("id", p.getId());
                            node.put("name", p.getName());
                            node.put("state", p.getState().name());
                            planNodes.add(node);
                        } else {
                            msg += String.format("%6d %-24s %s" + eol,
                                p.getId(),
                                p.getName(),
                                p.getState().toString());
                        }

                        howMany--;
                        planId = k.intValue() + 1;
                    }
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
            }
            if (shell.getJson()) {
                CommandResult result = new CommandSucceeds(
                    returnNode.toString());
                return Shell.toJsonReport(operation, result);
            }
            return msg;
        }

        @Override
        protected String getCommandSyntax() {
            return "show plans [-last] [-id ] [-from ] " +
                   "[-to ] [-num ] " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Shows details of the specified plan or lists all plans " +
                "that have been" + eolt + "created along with their " +
                "corresponding plan IDs and status." + eolt +
                eolt +
                "With no argument: lists the ten most recent plans." + eolt +
                "-last: shows details of the most recent plan" + eolt +
                "-id : shows details of the plan with the given id;" + eolt+
                "    if -num  is also given, list  plans," + eolt +
                "    starting with plan #." + eolt +
                "-num : sets the number of plans to list." + eolt +
                "    Defaults to 10." + eolt +
                "-from : lists plans after ." + eolt +
                "-to : lists plans before ." + eolt +
                "    Combining -from with -to describes the range" + eolt +
                "    between the two .  Otherwise -num applies." + eolt +
                "-json: return result in json format." + eolt +
                eolt +
                getDateFormatsUsage();
        }

        /*
         * Show details of a single plan.  TODO: add flags for varying details:
         * -tasks -finished, etc.
         */
        private String showSinglePlan(Shell shell, String[] args,
                                      CommandServiceAPI cs, CommandShell cmd)
            throws ShellException {

            int planId = 0;
            final boolean verbose = shell.getVerbose();

            try {
                if ("-last".equals(args[1])) {
                    planId = PlanCommand.PlanSubCommand.getLastPlanId(cs);
                    if (planId == 0) {
                        if (shell.getJson()) {
                            CommandResult result =
                                new CommandFails(lastPlanNotFound,
                                                 ErrorMessage.NOSQL_5200,
                                                 CommandResult.NO_CLEANUP_JOBS);
                            return Shell.toJsonReport(operation, result);
                        }
                        return lastPlanNotFound;
                    }
                } else if ("-id".equals(args[1])) {
                    if (args.length != 3) {
                        shell.badArgCount(this);
                    }
                    planId = parseUnsignedInt(args[2]);
                } else {
                    shell.unknownArgument(args[1], this);
                }

                long options = StatusReport.SHOW_FINISHED_BIT;
                if (verbose) {
                    options |= StatusReport.VERBOSE_BIT;
                }
                final String planStatus =
                    cs.getPlanStatus(
                        planId, options, shell.getJson());
                if (shell.getJson()) {
                    CommandResult result = new CommandSucceeds(planStatus);
                    return Shell.toJsonReport(operation, result);
                }
                return planStatus;
            } catch (RemoteException re) {
                cmd.noAdmin(re);
            }
            return cantGetHere;
        }
    }

    /*
     * ShowPools
     */
    @POST
    static final class ShowPools extends SubCommand {

        ShowPools() {
            super("pools", 3);
        }

        @Override
        public String execute(String[] args, Shell shell)
             throws ShellException {

            return new ShowPoolExecutor() {

                @Override
                public String allPoolsResult(List poolNames,
                                             CommandServiceAPI cs,
                                             Topology topo)
                    throws ShellException, RemoteException {
                    String res = "";
                    for (String pn : poolNames) {
                       res += pn + ": ";
                       for (StorageNodeId snid :
                           cs.getStorageNodePoolIds(pn)) {
                           DatacenterId dcid = topo.get(snid)
                               .getDatacenterId();
                           String dcName = topo.getDatacenterMap()
                               .get(dcid).getName();
                           res += snid.toString() + " zn:[id="
                               + dcid + " name="
                               + dcName + "], ";
                        }
                        res = res.substring(0, res.length() - 2);
                        res += eol;
                    }
                    return res;
                }

                @Override
                public String singlePoolResult(String poolName,
                                               CommandServiceAPI cs,
                                               Topology topo)
                    throws ShellException, RemoteException {
                    String res = poolName + ": ";
                    for (StorageNodeId snid :
                        cs.getStorageNodePoolIds(poolName)) {
                        DatacenterId dcid = topo.get(snid)
                            .getDatacenterId();
                        String dcName = topo.getDatacenterMap()
                            .get(dcid).getName();
                        res += snid.toString() + " zn:[id="
                            + dcid + " name="
                            + dcName + "], ";
                    }
                    res = res.substring(0, res.length() - 2);
                    res += eol;
                    return res;
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowPoolExecutor implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                Shell.checkHelp(args, ShowPools.this);
                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();

                String poolName = null;
                for (int i = 1; i < args.length; i++) {
                    final String arg = args[i];
                    if ("-name".equals(arg)) {
                        poolName = Shell.nextArg(args, i++, ShowPools.this);
                    } else {
                        throw new ShellUsageException(
                            "Invalid argument: " + arg, ShowPools.this);
                    }
                }

                try{
                    Topology topo = cs.getTopology();
                    /* show pools */
                    if (args.length == 1) {
                        final List poolNames =
                            cs.getStorageNodePoolNames();
                        return allPoolsResult(poolNames, cs, topo);
                    }
                    if (cs.getStorageNodePoolNames().contains(poolName)) {
                        return singlePoolResult(poolName, cs, topo);
                    }
                    throw new ShellArgumentException(
                        "Not a valid pool name: " + poolName);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }

            public abstract T
                allPoolsResult(List poolNames,
                               CommandServiceAPI cs,
                               Topology topo)
                throws ShellException, RemoteException;

            public abstract T
                singlePoolResult(String poolName,
                                 CommandServiceAPI cs,
                                 Topology topo)
                throws ShellException, RemoteException;
        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show pool");
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode poolArray = top.putArray("pools");
            return new ShowPoolExecutor() {

                @Override
                public ShellCommandResult
                    allPoolsResult(List poolNames,
                                   CommandServiceAPI cs,
                                   Topology topo)
                    throws ShellException, RemoteException {

                    for (String pn : poolNames) {
                        final ObjectNode poolNode =
                            JsonUtils.createObjectNode();
                        poolNode.put("poolName", pn);
                        final ArrayNode snArray = poolNode.putArray("sns");
                        for (StorageNodeId snid :
                            cs.getStorageNodePoolIds(pn)) {
                            final ObjectNode snNode =
                                JsonUtils.createObjectNode();
                            final DatacenterId dcid =
                                topo.get(snid).getDatacenterId();
                            final String dcName =
                                topo.getDatacenterMap().get(dcid).getName();
                            snNode.put("resourceId", snid.toString());
                            snNode.put("znId", dcid.toString());
                            snNode.put("zoneName", dcName);
                            snArray.add(snNode);
                        }
                        poolArray.add(poolNode);
                    }
                    scr.setReturnValue(top);
                    return scr;
                }

                @Override
                public ShellCommandResult
                    singlePoolResult(String poolName,
                                     CommandServiceAPI cs,
                                     Topology topo)
                    throws ShellException, RemoteException {
                    final ObjectNode poolNode =
                        JsonUtils.createObjectNode();
                    poolNode.put("poolName", poolName);
                    final ArrayNode snArray = poolNode.putArray("sns");
                    for (StorageNodeId snid :
                        cs.getStorageNodePoolIds(poolName)) {
                        final ObjectNode snNode =
                            JsonUtils.createObjectNode();
                        final DatacenterId dcid =
                            topo.get(snid).getDatacenterId();
                        final String dcName =
                            topo.getDatacenterMap().get(dcid).getName();
                        snNode.put("snId", snid.toString());
                        snNode.put("zoneId", dcid.toString());
                        snNode.put("zoneName", dcName);
                        snArray.add(snNode);
                    }
                    poolArray.add(poolNode);
                    scr.setReturnValue(top);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return "show pools [-name ] " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return "Lists the storage node pools";
        }
    }

    /*
     * TODO: Add filter flags
     */
    @POST
    private static final class ShowPerf extends SubCommand {

        private ShowPerf() {
            super("perf", 3);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            return new ShowPerfExecutor() {
                @Override
                public String
                    multiPerfResult(Map map) {
                    final ByteArrayOutputStream outStream =
                        new ByteArrayOutputStream();
                    final PrintStream out = new PrintStream(outStream);
                    out.println(PerfEvent.HEADER);
                    for (PerfEvent pe : map.values()) {
                        out.println(pe.getColumnFormatted());
                    }
                    return outStream.toString();
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowPerfExecutor implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();
                try {
                    final Map perfMap = cs.getPerfMap();
                    return multiPerfResult(perfMap);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }
            public abstract T multiPerfResult(
                Map map);
        }

        @Override
        public ShellCommandResult
            executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show perf");
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode perfArray = top.putArray("perfs");
            return new ShowPerfExecutor() {
                @Override
                public ShellCommandResult
                multiPerfResult(Map map) {
                    for (PerfEvent pe : map.values()) {
                        perfArray.add(pe.getColumnFormatted());
                    }
                    scr.setReturnValue(top);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return "show perf " +
                    CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Displays recent performance information for each " +
                "Replication Node.";
        }
    }

    @POST
    private static final class ShowSnapshots extends SubCommand {

        private ShowSnapshots() {
            super("snapshots", 3);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            return new ShowSnapshotExecutor() {

                @Override
                public String multiLineResult(String[] lines) {
                    String ret = "";
                    for (String ss : lines) {
                        ret += ss + eol;
                    }
                    return ret;
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowSnapshotExecutor implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                Shell.checkHelp(args, ShowSnapshots.this);
                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();
                StorageNodeId snid = null;
                for (int i = 1; i < args.length; i++) {
                    final String arg = args[i];
                    if ("-sn".equals(arg)) {
                        final String sn =
                            Shell.nextArg(args, i++, ShowSnapshots.this);
                        try {
                            snid = StorageNodeId.parse(sn);
                        } catch (IllegalArgumentException iae) {
                            invalidArgument(sn);
                        }
                    } else {
                        shell.unknownArgument(arg, ShowSnapshots.this);
                    }
                }
                try {
                    final Snapshot snapshot =
                        new Snapshot(cs, shell.getVerbose(),
                                     shell.getOutput());
                    String [] list = null;
                    if (snid != null) {
                        list = snapshot.listSnapshots(snid);
                    } else {
                        list = snapshot.listSnapshots();
                    }
                    return multiLineResult(list);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                } catch (IllegalArgumentException iae) {
                    throw new ShellException(iae.getMessage());
                }
                return null;
            }

            public abstract T multiLineResult(String[] lines);

        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show snapshot");
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode snapshotArray = top.putArray("snapshots");
            return new ShowSnapshotExecutor() {

                @Override
                public ShellCommandResult
                    multiLineResult(String[] lines) {
                    for (String ss : lines) {
                        snapshotArray.add(ss);
                    }
                    scr.setReturnValue(top);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return "show snapshots [-sn ] " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Lists snapshots on the specified Storage Node. If no " +
                "Storage Node" + eolt + "is specified one is chosen from " +
                "the store.";
        }
    }

    @POST
    private static final class ShowUpgradeOrder extends SubCommand {

        private ShowUpgradeOrder() {
            super("upgrade-order", 3);
        }

        @Override
        protected String getCommandSyntax() {
            return "show upgrade-order " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Lists the Storage Nodes which need to be upgraded in an " +
                "order that" + eolt + "prevents disruption to the store's " +
                "operation.";
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {
            return new ShowUpgradeOrderExecutor() {

                @Override
                public String retrieveUpgradeOrder(CommandServiceAPI cs,
                                                   KVVersion current,
                                                   KVVersion prerequisite)
                    throws RemoteException {
                    return cs.getUpgradeOrder(current, prerequisite);
                }

            }.commonExecute(args, shell);
        }

        private abstract class ShowUpgradeOrderExecutor
            implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();

                try {
                    /*
                     * Thus command gets the order for upgrading to the
                     * version of the CLI.
                     */
                    return
                        retrieveUpgradeOrder(cs,
                                             KVVersion.CURRENT_VERSION,
                                             KVVersion.PREREQUISITE_VERSION);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }

            public abstract T retrieveUpgradeOrder(CommandServiceAPI cs,
                                                   KVVersion current,
                                                   KVVersion prerequisite)
                throws RemoteException;
        }

        @Override
        public ShellCommandResult
            executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show upgrade-order");
            return new ShowUpgradeOrderExecutor() {

                @Override
                public ShellCommandResult
                    retrieveUpgradeOrder(CommandServiceAPI cs,
                                         KVVersion current,
                                         KVVersion prerequisite)
                    throws RemoteException {
                    final ObjectNode top = JsonUtils.createObjectNode();
                    List> result =
                        cs.getUpgradeOrderList(current, prerequisite);
                    final ArrayNode orderArray =
                        top.putArray("upgradeOrders");
                    for (Set set : result) {
                        final ObjectNode on = JsonUtils.createObjectNode();
                        final ArrayNode upgradeNodes =
                            on.putArray("upgradeNodes");
                        for (StorageNodeId id : set) {
                            upgradeNodes.add(id.toString());
                        }
                        orderArray.add(on);
                    }
                    scr.setReturnValue(top);
                    return scr;
                }

            }.commonExecute(args, shell);
        }

    }

    /*
     * ShowDatacenters
     */
    @POST
    static final class ShowDatacenters extends ShowZones {

        static final String dcCommandDeprecation =
            "The command:" + eol + eolt +
            "show datacenters" + eol + eol +
            "is deprecated and has been replaced by:" + eol + eolt +
            "show zones" + eol + eol;

        ShowDatacenters() {
            super("datacenters", 4);
        }

        /** This is a deprecated command. Return true. */
        @Override
        protected boolean isDeprecated() {
            return true;
        }

        /** Add deprecation message. */
        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            return dcCommandDeprecation + super.execute(args, shell);
        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            return super.executeJsonOutput(args, shell);
        }

        /** Add deprecation message. */
        @Override
        public String getCommandDescription() {
            return super.getCommandDescription() + eol + eolt +
                "This command is deprecated and has been replaced by:"
                + eol + eolt +
                "show zones";
        }
    }

    @POST
    static class ShowZones extends SubCommand {
        static final String ID_FLAG = "-zn";
        static final String NAME_FLAG = "-znname";
        static final String DESC_STR = "zone";
        static final String dcFlagsDeprecation =
            "The -dc and -dcname flags, and the dc ID format, are" +
            " deprecated" + eol +
            "and have been replaced by -zn, -znname, and zn." +
            eol + eol;

        private ShowZones() {
            super("zones", 4);
        }

        ShowZones(final String name, final int prefixLength) {
            super(name, prefixLength);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {
            return new ShowZoneExecutor() {

                @Override
                public String zoneResult(DatacenterId id,
                                         String nameFlag,
                                         Topology topo,
                                         Parameters params,
                                         String deprecatedDcFlagPrefix) {
                    final ByteArrayOutputStream outStream =
                        new ByteArrayOutputStream();
                    final PrintStream out = new PrintStream(outStream);
                    TopologyPrinter.printZoneInfo(
                        id, nameFlag, topo, out, params);
                    return deprecatedDcFlagPrefix + outStream;
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowZoneExecutor implements Executor {

            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                DatacenterId id = null;
                String nameFlag = null;
                boolean deprecatedDcFlag = false;

                Shell.checkHelp(args, ShowZones.this);
                if (args.length > 1) {
                    for (int i = 1; i < args.length; i++) {
                        if (CommandUtils.isDatacenterIdFlag(args[i])) {
                            try {
                                id = DatacenterId.parse(
                                    Shell.nextArg(args, i++, ShowZones.this));
                            } catch (IllegalArgumentException e) {
                                throw new ShellUsageException(
                                    "Invalid zone ID: " + args[i],
                                    ShowZones.this);
                            }
                            if (CommandUtils.isDeprecatedDatacenterId(
                                    args[i-1], args[i])) {
                                deprecatedDcFlag = true;
                            }
                        } else if (CommandUtils.
                                   isDatacenterNameFlag(args[i])) {
                            nameFlag =
                                Shell.nextArg(args, i++, ShowZones.this);
                            if (CommandUtils.isDeprecatedDatacenterName(
                                    args[i-1])) {
                                deprecatedDcFlag = true;
                            }
                        } else {
                            shell.unknownArgument(args[i], ShowZones.this);
                        }
                    }
                }

                final String deprecatedDcFlagPrefix =
                    !deprecatedDcFlag ? "" : dcFlagsDeprecation;

                final CommandShell cmd = (CommandShell) shell;
                final CommandServiceAPI cs = cmd.getAdmin();
                try {
                    final Topology topo = cs.getTopology();
                    final Parameters params = cs.getParameters();
                    return zoneResult(id, nameFlag, topo, params,
                                      deprecatedDcFlagPrefix);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                return null;
            }

            public abstract T
                zoneResult(DatacenterId id,
                           String nameFlag,
                           Topology topo,
                           Parameters params,
                           String deprecatedDcFlagPrefix)
                throws ShellException;
        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show zones");
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode zoneArray = top.putArray("zns");
            return new ShowZoneExecutor() {

                @Override
                public ShellCommandResult
                    zoneResult(DatacenterId id, String nameFlag,
                               Topology topo, Parameters params,
                               String deprecatedDcFlagPrefix)
                    throws ShellException{
                    final boolean showAll =
                        ((id == null) && (nameFlag == null) ? true : false);
                    Datacenter showZone = null;

                    /*
                     * Display zones, sorted by ID
                     */
                    final List dcList =
                        topo.getSortedDatacenters();
                    for (final Datacenter zone : dcList) {
                        if (showAll) {
                            zoneArray.add(zone.toJson());
                        } else {
                            if ((id != null) &&
                                id.equals(zone.getResourceId())) {
                                showZone = zone;
                                break;
                            } else if ((nameFlag != null) &&
                                nameFlag.equals(zone.getName())) {
                                showZone = zone;
                                break;
                            }
                        }
                    }
                    if (showAll) {
                        scr.setReturnValue(top);
                        return scr;
                    }

                    /*
                     * If showZone is null, then the id or name input is
                     * unknown
                     */
                    if (showZone == null) {
                        throw new ShellArgumentException(
                            DatacenterId.DATACENTER_PREFIX +
                            ": unknown id or name");
                    }

                    zoneArray.add(showZone.toJson());
                    scr.setReturnValue(top);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        @Override
        protected String getCommandSyntax() {
            return "show " + name +
                   " [" + ID_FLAG + "  | " + NAME_FLAG + " ] " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return
                "Lists the names of all " + DESC_STR + "s, or display " +
                "information about a" + eolt +
                "specific " + DESC_STR + ". If no " + DESC_STR + " is " +
                "specified, list the names" + eolt +
                "of all " + DESC_STR + "s. If a specific " + DESC_STR +
                " is specified using" + eolt +
                "either the " + DESC_STR + "'s id (via the '" + ID_FLAG +
                "' flag), or the " + DESC_STR + "'s" + eolt +
                "name (via the '" + NAME_FLAG + "' flag), then list " +
                "information such as the" + eolt + "names of the storage " +
                "nodes deployed to that " + DESC_STR + ".";
        }
    }

    /*
     * ShowTables
     */
    @POST
    private static final class ShowTables extends SubCommand {
        final static String CMD_TEXT = "tables";
        final static String TABLE_FLAG = "-name";
        final static String PARENT_FLAG = "-parent";
        final static String LEVEL_FLAG = "-level";
        final static String NAMESPACE_FLAG = "-namespace";

        private ShowTables() {
            super(CMD_TEXT, 3);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            return new ShowTableExecutor() {

                @Override
                public String emptyTableResult() {
                    return "No table found.";
                }

                @Override
                public String singleTableResult(TableImpl table,
                                                boolean showId)
                    throws ShellException {
                    return table.toJsonString(true);
                }

                @Override
                public String allTablesResult(Map tableMap,
                                              Integer maxLevel,
                                              boolean verbose)
                    throws ShellException {
                    return getAllTablesInfo(tableMap, maxLevel, verbose);
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowTableExecutor
            implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                String namespace = null;
                String tableName = null;
                String parentName = null;
                Integer maxLevel = null;
                Shell.checkHelp(args, ShowTables.this);
                if (args.length > 1) {
                    for (int i = 1; i < args.length; i++) {
                        if (TABLE_FLAG.equals(args[i])) {
                            tableName =
                                Shell.nextArg(args, i++, ShowTables.this);
                        } else if (PARENT_FLAG.equals(args[i])) {
                            parentName =
                                Shell.nextArg(args, i++, ShowTables.this);
                        } else if (NAMESPACE_FLAG.equals(args[i])) {
                            namespace =
                                Shell.nextArg(args, i++, ShowTables.this);
                        } else if (LEVEL_FLAG.equals(args[i])) {
                            String sLevel =
                                Shell.nextArg(args, i++, ShowTables.this);
                            maxLevel = parseUnsignedInt(sLevel);
                        } else {
                            shell.unknownArgument(args[i], ShowTables.this);
                        }
                    }
                }

                final CommandShell cmd = (CommandShell) shell;
                if (namespace == null) {
                    namespace = cmd.getNamespace();
                }
                final CommandServiceAPI cs = cmd.getAdmin();
                TableMetadata meta = null;
                try {
                    meta = cs.getMetadata(TableMetadata.class,
                                          MetadataType.TABLE);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                if (meta == null) {
                    return emptyTableResult();
                }

                /* Show the specified table's meta data. */
                if (tableName != null) {
                    TableImpl table = meta.getTable(namespace, tableName);
                    if (table == null) {
                        throw new ShellArgumentException("Table " +
                            NameUtils.makeQualifiedName(
                                namespace, tableName) +
                            " does not exist.");
                    }
                    return singleTableResult(table, cmd.getHidden());
                }

                /* Show multiple tables's meta data. */
                Map tableMap = null;
                boolean verbose = shell.getVerbose();
                if (parentName != null) {
                    TableImpl tbParent = meta.getTable(namespace, parentName);
                    if (tbParent == null) {
                        throw new ShellArgumentException("Table " +
                            NameUtils.makeQualifiedName(
                                namespace, parentName) +
                            " does not exist.");
                    }
                    tableMap = tbParent.getChildTables();
                } else {
                    tableMap = meta.getTables(namespace);
                }
                if (tableMap == null || tableMap.size() == 0) {
                    return emptyTableResult();
                }
                return allTablesResult(tableMap, maxLevel, verbose);
            }

            public abstract T emptyTableResult();

            public abstract T singleTableResult(TableImpl table,
                                                boolean showId)
                throws ShellException;

            public abstract T
                allTablesResult(Map tableMap,
                                Integer maxLevel, boolean verbose)
                throws ShellException;

        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show tables");

            return new ShowTableExecutor() {

                @Override
                public ShellCommandResult emptyTableResult() {
                    scr.setDescription("No table found.");
                    return scr;
                }

                @Override
                public ShellCommandResult
                    singleTableResult(TableImpl table,
                                      boolean showId)
                    throws ShellException {
                    final ObjectNode on =
                        CommandJsonUtils.createObjectNode();
                    if (showId) {
                        on.put("tableId", table.getId());
                    }
                    TableJsonUtils.toJsonString(table, on, false);
                    scr.setReturnValue(on);
                    return scr;
                }

                @Override
                public ShellCommandResult
                    allTablesResult(Map tableMap,
                                          Integer maxLevel,
                                          boolean verbose)
                    throws ShellException {
                    scr.setReturnValue(
                        getAllTablesInfoJson(tableMap, maxLevel, verbose));
                    return scr;
                }

            }.commonExecute(args, shell);
        }

        private String getAllTablesInfo(Map tableMap,
                                        Integer maxLevel, boolean verbose) {
            if (!verbose) {
                return "Tables: " + eolt +
                    getTableAndChildrenName(tableMap, 0, maxLevel);
            }
            return getTableAndChildrenMetaInfo(tableMap, 0, maxLevel);
        }

        private ObjectNode getAllTablesInfoJson(Map tableMap,
                                                Integer maxLevel,
                                                boolean verbose) {
            if (!verbose) {
                return getTableAndChildrenJson(tableMap, 0, maxLevel);
            }
            return getTableAndChildrenMetaInfoJson(tableMap, 0, maxLevel);
        }

        private String getTableAndChildrenName(Map tableMap,
                                               int curLevel,
                                               Integer maxLevel) {
            final String INDENT = "  ";
            String indent = "";
            StringBuilder sb = new StringBuilder();
            if (curLevel > 0) {
                for (int i = 0; i < curLevel; i++) {
                    indent += INDENT;
                }
            }
            for (Map.Entry entry: tableMap.entrySet()) {
                TableImpl table = (TableImpl)entry.getValue();
                sb.append(indent);
                sb.append(table.getFullNamespaceName());
                String desc = table.getDescription();
                if (desc != null && desc.length() > 0) {
                    sb.append(" -- ");
                    sb.append(desc);
                }
                sb.append(Shell.eolt);
                if (maxLevel != null && curLevel == maxLevel) {
                    continue;
                }
                Map childTabs = table.getChildTables();
                if (childTabs != null) {
                    sb.append(getTableAndChildrenName(
                                  childTabs, curLevel + 1, maxLevel));
                }
            }
            return sb.toString();
        }

        private ObjectNode getTableAndChildrenJson(Map tableMap,
                                                   int curLevel,
                                                   Integer maxLevel) {
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode tableArray = top.putArray("tables");
            for (Map.Entry entry: tableMap.entrySet()) {
                final ObjectNode tableNode = JsonUtils.createObjectNode();
                final TableImpl table = (TableImpl) entry.getValue();
                tableNode.put("name", table.getFullNamespaceName());
                tableNode.put("description", table.getDescription());
                if (maxLevel != null && curLevel == maxLevel) {
                    tableArray.add(tableNode);
                    continue;
                }
                Map childTabs = table.getChildTables();
                if (childTabs != null) {
                    tableNode.put("childTables",
                                  getTableAndChildrenJson(childTabs,
                                      curLevel + 1, maxLevel));
                }
                tableArray.add(tableNode);
            }
            return top;
        }

        private String getTableAndChildrenMetaInfo(Map tableMap,
                                                   int curLevel,
                                                   Integer maxLevel) {
            StringBuffer sb = new StringBuffer();
            for (Map.Entry entry: tableMap.entrySet()) {
                TableImpl table = (TableImpl)entry.getValue();
                sb.append(table.getFullName());
                sb.append(":");
                sb.append(Shell.eol);
                sb.append(table.toJsonString(true));
                sb.append(Shell.eol);
                if (maxLevel != null && curLevel == maxLevel) {
                    continue;
                }
                if (table.getChildTables() != null) {
                    sb.append(
                        getTableAndChildrenMetaInfo(
                            table.getChildTables(), curLevel++, maxLevel));
                }
            }
            return sb.toString();
        }

        private ObjectNode getTableAndChildrenMetaInfoJson(
            Map tableMap, int curLevel,
            Integer maxLevel) {
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode tableArray = top.putArray("tables");
            for (Map.Entry entry: tableMap.entrySet()) {
                TableImpl table = (TableImpl) entry.getValue();
                final ObjectNode tableNode = JsonUtils.createObjectNode();
                tableNode.put("name", table.getFullName());
                TableJsonUtils.toJsonString(table, tableNode, false);
                if (maxLevel != null && curLevel == maxLevel) {
                    tableArray.add(tableNode);
                    continue;
                }
                if (table.getChildTables() != null) {
                    tableNode.put("childTables",
                                  getTableAndChildrenMetaInfoJson(
                                      table.getChildTables(),
                                      curLevel++, maxLevel));
                }
                tableArray.add(tableNode);
            }
            return top;
        }

        @Override
        protected String getCommandSyntax() {
            return "show " + CMD_TEXT + " [" + TABLE_FLAG + " ] " +
                   "[" + PARENT_FLAG + " ] [" + LEVEL_FLAG +
                   " ] " +
                   CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return "Display table metadata.  By default the names of all " +
                "top-tables and" + eolt + "their child tables are listed.  " +
                "Top-level tables are those without" + eolt + "parents.  " +
                "The level of child tables can be limited by specifying the" +
                eolt + LEVEL_FLAG + " flag.  If a specific table is named " +
                "its detailed metadata is" + eolt + "displayed.  The table " +
                "name is an optionally namespace qualified" + eolt
                + "dot-separated name with the format " + eolt +
                "[ns:]tableName[.childTableName]*.  Flag " +
                PARENT_FLAG + " is used to show" + eolt +
                "all child tables for the given parent table.";
        }
    }

    /*
     * ShowIndexes
     */
    @POST
    private static final class ShowIndexes extends SubCommand {
        final static String CMD_TEXT = "indexes";
        final static String INDEX_FLAG = "-name";
        final static String TABLE_FLAG = "-table";
        final static String NAMESPACE_FLAG = "-namespace";

        private ShowIndexes() {
            super(CMD_TEXT, 3);
        }

        @Override
        public String execute(String[] args, Shell shell)
            throws ShellException {

            return new ShowIndexExecutor() {

                @Override
                public String singleIndexResult(Index index,
                                                String indexName,
                                                String tableName) {
                    return getIndexInfo(index);
                }

                @Override
                public String
                    allTableAllIndexResult(Map tableMap) {
                    return getAllTablesIndexesInfo(tableMap);
                }

                @Override
                public String tableIndexResult(TableImpl table)
                    throws ShellException{
                    String ret = getTableIndexesInfo(table);
                    if (ret == null) {
                        return "No Index found.";
                    }
                    return ret;
                }
            }.commonExecute(args, shell);
        }

        private abstract class ShowIndexExecutor implements Executor {
            @Override
            public T commonExecute(String[] args, Shell shell)
                throws ShellException {
                String namespace = null;
                String tableName = null;
                String indexName = null;
                Shell.checkHelp(args, ShowIndexes.this);
                if (args.length > 1) {
                    for (int i = 1; i < args.length; i++) {
                        if (TABLE_FLAG.equals(args[i])) {
                            tableName =
                                Shell.nextArg(args, i++, ShowIndexes.this);
                        } else if (NAMESPACE_FLAG.equals(args[i])) {
                            namespace =
                                Shell.nextArg(args, i++, ShowIndexes.this);
                        } else if (INDEX_FLAG.equals(args[i])) {
                            indexName =
                                Shell.nextArg(args, i++, ShowIndexes.this);
                        } else if (TABLE_FLAG.equals(args[i])) {
                            tableName =
                                Shell.nextArg(args, i++, ShowIndexes.this);
                        } else {
                            shell.unknownArgument(args[i], ShowIndexes.this);
                        }
                    }
                }

                if (indexName != null && tableName == null) {
                    shell.requiredArg(TABLE_FLAG, ShowIndexes.this);
                }

                final CommandShell cmd = (CommandShell) shell;
                if (namespace == null) {
                    namespace = cmd.getNamespace();
                }
                final CommandServiceAPI cs = cmd.getAdmin();
                TableMetadata meta = null;
                try {
                    meta = cs.getMetadata(TableMetadata.class,
                                          MetadataType.TABLE);
                } catch (RemoteException re) {
                    cmd.noAdmin(re);
                }
                if (meta == null) {
                    throw new ShellArgumentException("No table found.");
                }

                if (tableName != null) {
                    TableImpl table = meta.getTable(namespace, tableName);
                    if (table == null) {
                        throw new ShellArgumentException("Table " +
                            NameUtils.makeQualifiedName(namespace, tableName) +
                            " does not exist.");
                    }
                    if (indexName != null) {
                        Index index = table.getIndex(indexName);
                        if (index == null) {
                            throw new ShellArgumentException(
                                "Index " + indexName + " on table " +
                                tableName + " does not exist.");
                        }
                        return singleIndexResult(index,
                                                 indexName, tableName);
                    }
                    return tableIndexResult(table);
                }

                Map tableMap = null;
                tableMap = meta.getTables();
                if (tableMap == null || tableMap.isEmpty()) {
                    throw new ShellArgumentException("No table found.");
                }
                return allTableAllIndexResult(tableMap);
            }

            public abstract T singleIndexResult(Index index,
                                                String indexName,
                                                String tableName);

            public abstract T
                allTableAllIndexResult(Map tableMap);

            public abstract T
                tableIndexResult(TableImpl table) throws ShellException;
        }

        @Override
        public ShellCommandResult executeJsonOutput(String[] args, Shell shell)
            throws ShellException {
            final ShellCommandResult scr =
                ShellCommandResult.getDefault("show index");
            return new ShowIndexExecutor() {

                @Override
                public ShellCommandResult
                    singleIndexResult(Index index,
                                      String indexName,
                                      String tableName) {
                    scr.setReturnValue(getIndexInfoJson(index));
                    return scr;
                }

                @Override
                public ShellCommandResult
                    allTableAllIndexResult(Map tableMap) {
                    scr.setReturnValue(getAllTablesIndexesInfoJson(tableMap));
                    return scr;
                }

                @Override
                public ShellCommandResult
                    tableIndexResult(TableImpl table)  throws ShellException {
                    final ObjectNode on =
                        getTableIndexesInfoJson(table);
                    if (on == null) {
                        scr.setDescription("No Index found.");
                        return scr;
                    }
                    scr.setReturnValue(on);
                    return scr;
                }
            }.commonExecute(args, shell);
        }

        private String getAllTablesIndexesInfo(Map tableMap) {
            StringBuilder sb = new StringBuilder();
            for (Entry entry: tableMap.entrySet()) {
                Table table = entry.getValue();
                String ret = getTableIndexesInfo(table);
                if (ret == null) {
                    continue;
                }
                if (sb.length() > 0) {
                    sb.append(eol);
                }
                sb.append(ret);
                sb.append(eol);
                if (table.getChildTables() != null) {
                    ret = getAllTablesIndexesInfo(table.getChildTables());
                    if (ret.length() > 0) {
                        sb.append(eol);
                        sb.append(ret);
                    }
                }
            }
            return sb.toString();
        }

        private ObjectNode
            getAllTablesIndexesInfoJson(Map tableMap) {
            final ObjectNode top = JsonUtils.createObjectNode();
            final ArrayNode tableArray = top.putArray("tables");
            for (Entry entry: tableMap.entrySet()) {
                Table table = entry.getValue();
                final ObjectNode on = JsonUtils.createObjectNode();
                final ObjectNode ret = getTableIndexesInfoJson(table);
                if (ret == null) {
                    continue;
                }
                on.put("table", ret);
                final ArrayNode childArray = on.putArray("childTable");
                if (table.getChildTables() != null) {
                    childArray.add(
                        getAllTablesIndexesInfoJson(table.getChildTables()));
                }
                tableArray.add(on);
            }
            return top;
        }

        private String getTableIndexesInfo(Table table) {
            Map map = table.getIndexes();
            if (map == null || map.isEmpty()) {
                return null;
            }
            StringBuilder sb = new StringBuilder();
            sb.append("Indexes on table ");
            sb.append(table.getFullName());
            for (Entry entry: map.entrySet()) {
                sb.append(eol);
                sb.append(getIndexInfo(entry.getValue()));
            }
            return sb.toString();
        }

        private ObjectNode getTableIndexesInfoJson(Table table) {
            Map map = table.getIndexes();
            if (map == null || map.isEmpty()) {
                return null;
            }
            final ObjectNode on = JsonUtils.createObjectNode();
            on.put("tableName", table.getFullName());
            final ArrayNode indexArray = on.putArray("indexes");
            for (Entry entry: map.entrySet()) {
                indexArray.add(getIndexInfoJson(entry.getValue()));
            }
            return on;
        }

        private String getIndexInfo(Index index) {
            StringBuilder sb = new StringBuilder();
            sb.append(Shell.tab);
            sb.append(index.getName());
            sb.append(" (");
            boolean first = true;
            for (String s : index.getFields()) {
                if (!first) {
                    sb.append(", ");
                } else {
                    first = false;
                }
                sb.append(s);
            }
            sb.append(")");

            if (index.getType().equals(Index.IndexType.TEXT)) {
                sb.append(", type: " + Index.IndexType.TEXT);
            } else if (index.getType().equals(Index.IndexType.SECONDARY)) {
                sb.append(", type: " + Index.IndexType.SECONDARY);
            }

            if (index.getDescription() != null) {
                sb.append(" -- ");
                sb.append(index.getDescription());
            }
            return sb.toString();
        }

        private ObjectNode getIndexInfoJson(Index index) {
            final ObjectNode on = JsonUtils.createObjectNode();
            on.put("name", index.getName());
            final ArrayNode fieldArray = on.putArray("fields");
            for (String s : index.getFields()) {
                fieldArray.add(s);
            }

            on.put("type", index.getType().name());
            on.put("description", index.getDescription());
            return on;
        }

        @Override
        protected String getCommandSyntax() {
            return "show " + CMD_TEXT + " [" + TABLE_FLAG + " ] " +
                    "[" + INDEX_FLAG + " ] " +
                    CommandParser.getJsonUsage();
        }

        @Override
        protected String getCommandDescription() {
            return "Display index metadata. By default the indexes metadata " +
                "of all tables" + eolt +
                "are listed.  If a specific table is named its indexes " +
                "metadata are" + eolt +
                "displayed, if a specific index of the table is named its " +
                "metadata is " + eolt +
                "displayed.";
        }
    }

    /*
     * ShowUsers
     *
     * Print the user information stored in the security metadata copy. If
     * no user name is specified, a brief information of all users will be
     * printed. Otherwise, only the specified user will be printed.
     *
     * While showing the information of all users, the format will be:
     * 
"user: id=xxx name=xxx"
* For showing the details of a specified user, it will be: *
"user: id=xxx name=xxx state=xxx type=xxx retained-passwd=xxxx" */ @POST private static final class ShowUsers extends SubCommand { static final String NAME_FLAG = "-name"; static final String DESC_STR = "user"; private ShowUsers() { super("users", 4); } @Override public String execute(String[] args, Shell shell) throws ShellException { return new ShowUserExecutor() { @Override public String noUserResult(String message) { return message; } @Override public String multiUserResult( Collection usersDesc) { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); final PrintStream out = new PrintStream(outStream); for (final UserDescription desc : usersDesc) { out.println("user: " + desc.brief()); } return outStream.toString(); } }.commonExecute(args, shell); } private abstract class ShowUserExecutor implements Executor { @Override public T commonExecute(String[] args, Shell shell) throws ShellException { String nameFlag = null; Shell.checkHelp(args, ShowUsers.this); if (args.length > 1) { for (int i = 1; i < args.length; i++) { if ("-name".equals(args[i])) { nameFlag = Shell.nextArg(args, i++, ShowUsers.this); if (nameFlag == null || nameFlag.isEmpty()) { throw new ShellUsageException( "User name could not be empty.", ShowUsers.this); } } else { shell.unknownArgument(args[i], ShowUsers.this); } } } final CommandShell cmd = (CommandShell) shell; final CommandServiceAPI cs = cmd.getAdmin(); try { final Map userDescMap = cs.getUsersDescription(); if (userDescMap == null || userDescMap.isEmpty()) { return noUserResult("No users."); } if (nameFlag != null) { /* Print details for a user */ final UserDescription desc = userDescMap.get(nameFlag); final String message = desc == null ? "User with name of " + nameFlag + " not found." : "user: " + desc.details(); throw new ShellArgumentException(message); } /* Print summary for all users */ final Collection usersDesc = userDescMap.values(); return multiUserResult(usersDesc); } catch (RemoteException re) { cmd.noAdmin(re); } return null; } public abstract T noUserResult(String text); public abstract T multiUserResult(Collection usersDesc); } @Override public ShellCommandResult executeJsonOutput(String[] args, Shell shell) throws ShellException { final ShellCommandResult scr = ShellCommandResult.getDefault("show users"); final ObjectNode top = JsonUtils.createObjectNode(); final ArrayNode userArray = top.putArray("users"); return new ShowUserExecutor() { @Override public ShellCommandResult noUserResult(String text) { scr.setDescription(text); return scr; } @Override public ShellCommandResult multiUserResult( Collection usersDesc) { final ObjectNode on = JsonUtils.createObjectNode(); for (final UserDescription desc : usersDesc) { on.put("user", desc.brief()); userArray.add(on); } scr.setReturnValue(top); return scr; } }.commonExecute(args, shell); } @Override protected String getCommandSyntax() { return "show " + "users" + " [-name ] " + CommandParser.getJsonUsage(); } @Override protected String getCommandDescription() { return "Lists the names of all " + DESC_STR + "s, or displays " + "information about a" + eolt + "specific " + DESC_STR + ". If no " + DESC_STR + " is " + "specified, lists the names" + eolt + "of all " + DESC_STR + "s. If a " + DESC_STR + " is specified using the " + NAME_FLAG + " flag," + eolt + "then lists detailed information about the " + DESC_STR + "."; } } /* * ShowVersions * * Print client and server version information. */ @POST private static final class ShowVersions extends SubCommand { private ShowVersions() { super("versions", 3); } @Override public String execute(String[] args, Shell shell) throws ShellException { return new ShowVersionExecutor() { @Override public String versionResult(StorageNodeStatus status, String exceptionMessage, String adminHostName) { final StringBuilder sb = new StringBuilder(); sb.append("Client version: "); sb.append( KVVersion.CURRENT_VERSION.getNumericVersionString()); sb.append(eol); if (status == null) { sb.append("Cannot reach server at host "); sb.append(adminHostName); sb.append(": " + eol); sb.append(exceptionMessage); } else { sb.append("Server version: "); sb.append(status.getKVVersion()); } return sb.toString(); } }.commonExecute(args, shell); } private abstract class ShowVersionExecutor implements Executor { @Override public T commonExecute(String[] args, Shell shell) throws ShellException { CommandShell cmd = (CommandShell) shell; StorageNodeStatus status = null; String exceptionMessage = ""; try{ CommandServiceAPI cs = cmd.getAdmin(); LoadParameters adminConfig = cs.getParams(); int snId = adminConfig.getMap(ParameterState.SNA_TYPE). getOrZeroInt(ParameterState.COMMON_SN_ID); String storeName = adminConfig.getMap(ParameterState.GLOBAL_TYPE). get(ParameterState.COMMON_STORENAME).asString(); /* ping service doesn't need to login, * so set login manager as null here */ StorageNodeAgentAPI sna = RegistryUtils.getStorageNodeAgent( storeName, cmd.getAdminHostname(), cmd.getAdminPort(), new StorageNodeId(snId), null/*LoginManager*/); status = sna.ping(); } catch (RemoteException re) { exceptionMessage = re.toString(); } catch (NotBoundException e) { exceptionMessage = e.toString(); } return versionResult( status, exceptionMessage, cmd.getAdminHostname()); } public abstract T versionResult(StorageNodeStatus status, String exceptionMessage, String adminHostName); } @Override public ShellCommandResult executeJsonOutput(String[] args, Shell shell) throws ShellException { final ShellCommandResult scr = ShellCommandResult.getDefault("show versions"); return new ShowVersionExecutor() { @Override public ShellCommandResult versionResult(StorageNodeStatus status, String exceptionMessage, String adminHostName) { final ObjectNode on = JsonUtils.createObjectNode(); on.put( "clientVersion", KVVersion.CURRENT_VERSION.getNumericVersionString()); if (status != null) { final KVVersion version = status.getKVVersion(); on.put("serverVersion", version.getNumericVersionString()); if (version.getReleaseEdition() != null) { on.put("serverEdition", version.getReleaseEdition()); } } scr.setReturnValue(on); return scr; } }.commonExecute(args, shell); } @Override protected String getCommandSyntax() { return "show versions " + CommandParser.getJsonUsage(); } @Override protected String getCommandDescription() { return "Display client and connected server version information. " + eolt + "If you want to get all servers version, please use " + "ping instead."; } } /** * When specifying event timestamps, these formats are accepted. */ private static String[] dateFormats = { "yyyy-MM-dd HH:mm:ss.SSS", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd", "MM-dd-yyyy HH:mm:ss.SSS", "MM-dd-yyyy HH:mm:ss", "MM-dd-yyyy HH:mm", "MM-dd-yyyy", "HH:mm:ss.SSS", "HH:mm:ss", "HH:mm" }; private static String getDateFormatsUsage() { String usage = " can be given in the following formats," + eolt + "which are interpreted in the UTC time zone." + eolt; for (String fs : dateFormats) { usage += eolt + " " + fs; } return usage; } /** * Apply the above formats in sequence until one of them matches. */ private static Date parseTimestamp(String s, ShellCommand command) throws ShellUsageException { TimeZone tz = TimeZone.getTimeZone("UTC"); Date r = null; for (String fs : dateFormats) { final DateFormat f = new SimpleDateFormat(fs); f.setTimeZone(tz); f.setLenient(false); try { r = f.parse(s); break; } catch (ParseException pe) /* CHECKSTYLE:OFF */ { } /* CHECKSTYLE:ON */ } if (r == null) { throw new ShellUsageException ("Invalid date format: " + s, command); } /* * If the date parsed is in the distant past (i.e., in January 1970) * then the string lacked a year/month/day. We'll be friendly and * interpret the time as being in the recent past, that is, today. */ final Calendar rcal = Calendar.getInstance(tz); rcal.setTime(r); if (rcal.get(Calendar.YEAR) == 1970) { final Calendar nowCal = Calendar.getInstance(); nowCal.setTime(new Date()); rcal.set(nowCal.get(Calendar.YEAR), nowCal.get(Calendar.MONTH), nowCal.get(Calendar.DAY_OF_MONTH)); /* If the resulting time is in the future, subtract one day. */ if (rcal.after(nowCal)) { rcal.add(Calendar.DAY_OF_MONTH, -1); } r = rcal.getTime(); } return r; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy