DIRKRB-716 Implement local admin tool.
diff --git a/has-project/has-common/src/main/java/org/apache/kerby/has/common/Hadmin.java b/has-project/has-common/src/main/java/org/apache/kerby/has/common/Hadmin.java
index bff7760..15c3fea 100644
--- a/has-project/has-common/src/main/java/org/apache/kerby/has/common/Hadmin.java
+++ b/has-project/has-common/src/main/java/org/apache/kerby/has/common/Hadmin.java
@@ -33,4 +33,6 @@
     File getKeytabByHostAndRole(String host, String role) throws HasException;
 
     void getHostRoles();
+
+    void setEnableOfConf(String isEnable) throws HasException;
 }
diff --git a/has-project/has-server/src/main/java/org/apache/kerby/has/server/admin/LocalHadmin.java b/has-project/has-server/src/main/java/org/apache/kerby/has/server/admin/LocalHadmin.java
index 0ece5f8..acf7855 100644
--- a/has-project/has-server/src/main/java/org/apache/kerby/has/server/admin/LocalHadmin.java
+++ b/has-project/has-server/src/main/java/org/apache/kerby/has/server/admin/LocalHadmin.java
@@ -21,6 +21,7 @@
 
 import org.apache.kerby.has.common.Hadmin;
 import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.common.util.HasUtil;
 import org.apache.kerby.has.server.HasServer;
 import org.apache.kerby.has.server.web.HostRoleType;
 import org.apache.kerby.kerberos.kerb.KrbException;
@@ -35,6 +36,7 @@
 import org.slf4j.LoggerFactory;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -43,6 +45,7 @@
 
     private final ServerSetting serverSetting;
     private LocalKadmin kadmin;
+    private File confDir;
 
     public LocalHadmin(HasServer hasServer) throws KrbException {
         if (hasServer.getKdcServer() == null) {
@@ -60,6 +63,7 @@
      * @throws KrbException e
      */
     public LocalHadmin(File confDir) throws KrbException {
+        this.confDir = confDir;
         KdcConfig tmpKdcConfig = KdcUtil.getKdcConfig(confDir);
         if (tmpKdcConfig == null) {
             tmpKdcConfig = new KdcConfig();
@@ -141,4 +145,19 @@
             }
         }
     }
+
+    @Override
+    public void setEnableOfConf(String isEnable) throws HasException {
+        File hasConf = new File(confDir, "has-server.conf");
+        if (!hasConf.exists()) {
+            System.err.println("has-server.conf is not exists.");
+            return;
+        }
+        try {
+            HasUtil.setEnableConf(hasConf, isEnable);
+        } catch (IOException e) {
+            System.err.println(e.getMessage());
+            return;
+        }
+    }
 }
diff --git a/kerby-dist/has-dist/bin/admin-remote.sh b/kerby-dist/has-dist/bin/admin-remote.sh
index 6aebc81..cd14615 100644
--- a/kerby-dist/has-dist/bin/admin-remote.sh
+++ b/kerby-dist/has-dist/bin/admin-remote.sh
@@ -17,7 +17,7 @@
 # limitations under the License.
 
 CONF_DIR=$1
-APP_MAIN=org.apache.kerby.kerberos.tool.admin.AdminRemoteTool
+APP_MAIN=org.apache.kerby.kerberos.tool.admin.remote.AdminRemoteTool
 
 # Reset HAS_CONF_DIR if CONF_DIR not null
 if [ "$CONF_DIR" != "" ]; then
diff --git a/kerby-tool/has-tool/pom.xml b/kerby-tool/has-tool/pom.xml
index 920174d..0dc5ca4 100644
--- a/kerby-tool/has-tool/pom.xml
+++ b/kerby-tool/has-tool/pom.xml
@@ -19,14 +19,29 @@
         </dependency>
         <dependency>
             <groupId>org.apache.kerby</groupId>
+            <artifactId>has-server</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.kerby</groupId>
             <artifactId>has-common</artifactId>
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.kerby</groupId>
+            <artifactId>kdc-tool</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
             <groupId>org.jline</groupId>
             <artifactId>jline</artifactId>
             <version>${jline.version}</version>
         </dependency>
+        <dependency>
+            <groupId>com.jcraft</groupId>
+            <artifactId>jsch</artifactId>
+            <version>${jsch.version}</version>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/AdminLocalTool.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/AdminLocalTool.java
new file mode 100644
index 0000000..f4cc879
--- /dev/null
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/AdminLocalTool.java
@@ -0,0 +1,322 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.tool.admin.local;
+
+import org.apache.kerby.KOptions;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHadmin;
+import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.KadminOption;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadmin;
+import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
+import org.apache.kerby.kerberos.tool.admin.local.cmd.AddPrincipalsAndDeployKeytabsCommand;
+import org.apache.kerby.kerberos.tool.admin.local.cmd.AddPrincipalsCommand;
+import org.apache.kerby.kerberos.tool.admin.local.cmd.DeployHTTPSCertsCommand;
+import org.apache.kerby.kerberos.tool.admin.local.cmd.DisableConfigureCommand;
+import org.apache.kerby.kerberos.tool.admin.local.cmd.EnableConfigureCommand;
+import org.apache.kerby.kerberos.tool.admin.local.cmd.ExportKeytabsCommand;
+import org.apache.kerby.kerberos.tool.admin.local.cmd.GetHostRolesCommand;
+import org.apache.kerby.kerberos.tool.admin.local.cmd.HadminCommand;
+import org.apache.kerby.kerberos.tool.kadmin.AuthUtil;
+import org.apache.kerby.kerberos.tool.kadmin.Krb5Conf;
+import org.apache.kerby.kerberos.tool.kadmin.ToolUtil;
+import org.apache.kerby.kerberos.tool.kadmin.command.AddPrincipalCommand;
+import org.apache.kerby.kerberos.tool.kadmin.command.ChangePasswordCommand;
+import org.apache.kerby.kerberos.tool.kadmin.command.DeletePrincipalCommand;
+import org.apache.kerby.kerberos.tool.kadmin.command.GetPrincipalCommand;
+import org.apache.kerby.kerberos.tool.kadmin.command.KadminCommand;
+import org.apache.kerby.kerberos.tool.kadmin.command.KeytabAddCommand;
+import org.apache.kerby.kerberos.tool.kadmin.command.ListPrincipalCommand;
+import org.apache.kerby.kerberos.tool.kadmin.command.RenamePrincipalCommand;
+import org.apache.kerby.util.OSUtil;
+import org.jline.reader.Completer;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReaderBuilder;
+import org.jline.reader.impl.completer.StringsCompleter;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.LoginException;
+import java.io.File;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Map;
+import java.util.Set;
+
+public class AdminLocalTool {
+    private static final Logger LOG = LoggerFactory.getLogger(AdminLocalTool.class);
+    private static File confDir;
+
+    private static final String PROMPT = "admin.local";
+    private static  final String USAGE = (OSUtil.isWindows()
+            ? "Usage: bin\\admin-local.cmd" : "Usage: sh bin/admin-local.sh")
+            + " <conf-dir> <-c cache_name>|<-k keytab>\n"
+            + "\tExample:\n"
+            + "\t\t"
+            + (OSUtil.isWindows()
+            ? "bin\\admin-local.cmd" : "sh bin/admin-local.sh")
+            + " conf -k admin.keytab\n";
+
+    private static void printUsage(String error) {
+        System.err.println(error + "\n");
+        System.err.println(USAGE);
+        System.exit(-1);
+    }
+
+    private static final String LEGAL_COMMANDS = "Available commands are: "
+        + "\n"
+        + "add_principal, addprinc\n"
+        + "                         Add principal\n"
+        + "delete_principal, delprinc\n"
+        + "                         Delete principal\n"
+        + "rename_principal, renprinc\n"
+        + "                         Rename principal\n"
+        + "change_password, cpw\n"
+        + "                         Change password\n"
+        + "get_principal, getprinc\n"
+        + "                         Get principal\n"
+        + "list_principals, listprincs\n"
+        + "                         List principals\n"
+        + "ktadd, xst\n"
+        + "                         Add entry(s) to a keytab\n"
+        + "get_hostroles, hostroles\n"
+        + "                         Get hostRoles\n"
+        + "export_keytabs, expkeytabs\n"
+        + "                         Export keytabs\n"
+        + "create_principals, creprincs\n"
+        + "                         Create principals\n"
+        + "enable_configure, enable\n"
+        + "                         Enable configure\n"
+        + "disable_configure, disable\n"
+        + "                         Disable configure\n"
+        + "deploy_keytabs, depkeytabs\n"
+        + "                         Deploy keytabs\n"
+        + "deploy_https, dephttps\n"
+        + "                         Deploy https\n";
+
+    private static void execute(LocalKadmin kadmin, LocalHadmin hadmin, String input) throws HasException {
+        // Omit the leading and trailing whitespace.
+        input = input.trim();
+        if (input.startsWith("cmd")) {
+            System.out.println(LEGAL_COMMANDS);
+            return;
+        }
+
+        String[] items = input.split("\\s+");
+        String cmd = items[0];
+        HadminCommand hadminExecutor = null;
+        KadminCommand kadminExecutor = null;
+        if (cmd.startsWith("add_principal")
+            || cmd.startsWith("addprinc")) {
+            kadminExecutor = new AddPrincipalCommand(kadmin);
+        } else if (cmd.startsWith("delete_principal")
+            || cmd.startsWith("delprinc")) {
+            kadminExecutor = new DeletePrincipalCommand(kadmin);
+        } else if (cmd.startsWith("rename_principal")
+            || cmd.startsWith("renprinc")) {
+            kadminExecutor = new RenamePrincipalCommand(kadmin);
+        } else if (cmd.startsWith("change_password")
+                || cmd.startsWith("cpw")) {
+            kadminExecutor = new ChangePasswordCommand(kadmin);
+        } else if (cmd.startsWith("list_principals")
+            || cmd.startsWith("listprincs")) {
+            kadminExecutor = new ListPrincipalCommand(kadmin);
+        } else if (cmd.startsWith("get_principal")
+                || cmd.startsWith("getprinc")) {
+            kadminExecutor = new GetPrincipalCommand(kadmin);
+        } else if (cmd.startsWith("ktadd")
+            || cmd.startsWith("xst")) {
+            kadminExecutor = new KeytabAddCommand(kadmin);
+        } else if (cmd.startsWith("get_hostroles")
+            || cmd.startsWith("hostroles")) {
+            hadminExecutor = new GetHostRolesCommand(hadmin);
+        } else if (cmd.startsWith("create_principals")
+            || cmd.startsWith("creprincs")) {
+            hadminExecutor = new AddPrincipalsCommand(hadmin);
+        } else if (cmd.startsWith("export_keytabs")
+            || cmd.startsWith("expkeytabs")) {
+            hadminExecutor = new ExportKeytabsCommand(hadmin);
+        } else if (cmd.startsWith("enable_configure")
+            || cmd.startsWith("enable")) {
+            hadminExecutor = new EnableConfigureCommand(hadmin);
+        } else if (cmd.startsWith("disable_configure")
+            || cmd.startsWith("disable")) {
+            hadminExecutor = new DisableConfigureCommand(hadmin);
+        } else if (cmd.startsWith("deploy_keytabs")
+            || cmd.startsWith("depkeytabs")) {
+            hadminExecutor = new AddPrincipalsAndDeployKeytabsCommand(hadmin);
+        } else if (cmd.startsWith("deploy_https")
+            || cmd.startsWith("dephttps")) {
+            hadminExecutor = new DeployHTTPSCertsCommand(hadmin);
+        } else {
+            System.out.println(LEGAL_COMMANDS);
+            return;
+        }
+        if (kadminExecutor != null) {
+            kadminExecutor.execute(input);
+        } else if (hadminExecutor != null) {
+            hadminExecutor.execute(items);
+        }
+    }
+
+    private static File getConfDir(String[] args) {
+        String envDir;
+        confDir = new File(args[0]);
+        if (confDir == null || !confDir.exists()) {
+            try {
+                Map<String, String> mapEnv = System.getenv();
+                envDir = mapEnv.get("KRB5_KDC_DIR");
+            } catch (SecurityException e) {
+                envDir = null;
+            }
+            if (envDir != null) {
+                confDir = new File(envDir);
+            } else {
+                confDir = new File("/etc/kerby/"); // for Linux. TODO: fix for Win etc.
+            }
+
+            if (!confDir.exists()) {
+                throw new RuntimeException("Can not locate KDC backend directory "
+                        + confDir.getAbsolutePath());
+            }
+        }
+        LOG.info("Conf dir:" + confDir.getAbsolutePath());
+        return confDir;
+    }
+
+    public static void main(String[] args) {
+
+        if (args.length < 2) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        LocalKadmin kadmin;
+        try {
+            kadmin = new LocalKadminImpl(getConfDir(args));
+        } catch (KrbException e) {
+            System.err.println("Failed to init Kadmin due to " + e.getMessage());
+            return;
+        }
+
+        LocalHadmin hadmin;
+        try {
+            hadmin = new LocalHadmin(getConfDir(args));
+        } catch (KrbException e) {
+            System.err.println("Failed to init Hadmin due to " + e.getMessage());
+            return;
+        }
+
+        try {
+            Krb5Conf krb5Conf = new Krb5Conf(confDir, kadmin.getKdcConfig());
+            krb5Conf.initKrb5conf();
+        } catch (IOException e) {
+            System.err.println("Failed to make krb5.conf." + e.getMessage());
+        }
+
+        String kadminPrincipal = kadmin.getKadminPrincipal();
+
+
+        KOptions kOptions = ToolUtil.parseOptions(args, 1, args.length - 1);
+        if (kOptions == null) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        Subject subject = null;
+        if (kOptions.contains(KadminOption.CCACHE)) {
+            File ccFile = kOptions.getFileOption(KadminOption.CCACHE);
+            if (ccFile == null || !ccFile.exists()) {
+                printUsage("Need the valid credentials cache file.");
+                return;
+            }
+            try {
+                subject = AuthUtil.loginUsingTicketCache(kadminPrincipal, ccFile);
+            } catch (LoginException e) {
+                System.err.println("Could not login with: " + kadminPrincipal
+                    + e.getMessage());
+                return;
+            }
+        } else if (kOptions.contains(KadminOption.K)) {
+            File keyTabFile = new File(kOptions.getStringOption(KadminOption.K));
+            if (keyTabFile == null || !keyTabFile.exists()) {
+                printUsage("Need the valid keytab file.");
+                return;
+            }
+            try {
+                subject = AuthUtil.loginUsingKeytab(kadminPrincipal, keyTabFile);
+            } catch (LoginException e) {
+                System.err.println("Could not login with: " + kadminPrincipal
+                    + e.getMessage());
+                return;
+            }
+        } else {
+            printUsage("No credentials cache file or keytab file for authentication.");
+        }
+        if (subject != null) {
+            Principal adminPrincipal = new KerberosPrincipal(kadminPrincipal);
+            Set<Principal> princSet = subject.getPrincipals();
+            if (princSet == null || princSet.isEmpty()) {
+                printUsage("The principals in subject is empty.");
+                return;
+            }
+            if (princSet.contains(adminPrincipal)) {
+                System.out.println("Login successful for user: " + kadminPrincipal);
+            } else {
+                printUsage("Login failure for " + kadminPrincipal);
+                return;
+            }
+        } else {
+            printUsage("The subject is null, login failure for " + kadminPrincipal);
+            return;
+        }
+
+        System.out.println("enter \"cmd\" to see legal commands.");
+
+        Completer completer = new StringsCompleter("add_principal",
+                "delete_principal", "rename_principal", "change_password", "list_principals",
+                "get_principal", "ktadd", "get_hostroles", "export_keytabs", "add_principals",
+                "enable_configure", "disable_configure", "deploy_keytabs", "deploy_https");
+
+        Terminal terminal = null;
+        try {
+            terminal = TerminalBuilder.terminal();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        LineReader lineReader = LineReaderBuilder.builder().completer(completer).terminal(terminal).build();
+
+        while (true) {
+            try {
+                String line = lineReader.readLine(PROMPT + ": ");
+                if ("quit".equals(line) || "exit".equals(line) || "q".equals(line)) {
+                    break;
+                }
+                execute(kadmin, hadmin, line);
+            } catch (HasException e) {
+                System.err.println(e.getMessage());
+            }
+        }
+    }
+}
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/AddPrincipalsAndDeployKeytabsCommand.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/AddPrincipalsAndDeployKeytabsCommand.java
new file mode 100644
index 0000000..865f1c6
--- /dev/null
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/AddPrincipalsAndDeployKeytabsCommand.java
@@ -0,0 +1,177 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.tool.admin.local.cmd;
+
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpException;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHadmin;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONException;
+import org.codehaus.jettison.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class AddPrincipalsAndDeployKeytabsCommand extends HadminCommand {
+    private static final String USAGE
+        = "\nUsage: deploy_keytabs [HostRoles-File] [Where-to-Deploy] [SSH-Port] [UserName] [Password]\n"
+        + "\tExample:\n"
+        + "\t\tdeploy_keytabs hostroles.txt /etc/has/ 22 username password\n";
+
+    public AddPrincipalsAndDeployKeytabsCommand(LocalHadmin hadmin) {
+        super(hadmin);
+    }
+
+    @Override
+    public void execute(String[] items) throws HasException {
+
+        if (items.length < 5 || items.length > 6) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        File hostfile = new File(items[1]);
+        if (!hostfile.exists()) {
+            throw new HasException("HostRoles file is not exists.");
+        }
+        String pathToDeploy = items[2];
+        int port = Integer.valueOf(items[3]);
+        String username = items[4];
+        String password = "";
+        if (items.length == 6) {
+            password = items[5];
+        }
+
+        BufferedReader reader;
+        try {
+            reader = new BufferedReader(new FileReader(hostfile));
+        } catch (FileNotFoundException e) {
+            throw new HasException("The host roles file: " + hostfile + "is not exist. " + e.getMessage());
+        }
+        StringBuilder sb = new StringBuilder();
+        String tempString;
+        try {
+            while ((tempString = reader.readLine()) != null) {
+                sb.append(tempString);
+            }
+        } catch (IOException e) {
+            throw new HasException("Failed to read file: " + e.getMessage());
+        }
+        JSONArray hostArray;
+        try {
+            hostArray = new JSONObject(sb.toString()).optJSONArray("HOSTS");
+        } catch (JSONException e) {
+            throw new HasException(e.getMessage());
+        }
+        for (int i = 0; i < hostArray.length(); i++) {
+            JSONObject host;
+            try {
+                host = (JSONObject) hostArray.get(i);
+            } catch (JSONException e) {
+                throw new HasException(e.getMessage());
+            }
+            String hostname;
+            try {
+                hostname = host.getString("name");
+            } catch (JSONException e) {
+                throw new HasException(e.getMessage());
+            }
+            String[] roles;
+            try {
+                roles = host.getString("hostRoles").split(",");
+            } catch (JSONException e) {
+                throw new HasException(e.getMessage());
+            }
+            List<File> keytabs = new ArrayList<>();
+            for (String role : roles) {
+                // Add principal.
+                System.out.println(getHadmin().addPrincByRole(hostname,
+                    role.toUpperCase()));
+                // Export keytab
+                File keytab = getHadmin().getKeytabByHostAndRole(hostname, role);
+
+                keytabs.add(keytab);
+            }
+
+            JSch jsch = new JSch();
+            Session session;
+            try {
+                session = jsch.getSession(username, hostname, port);
+            } catch (JSchException e) {
+                throw new HasException(e.getMessage());
+            }
+            session.setPassword(password);
+
+            java.util.Properties config = new java.util.Properties();
+            config.put("StrictHostKeyChecking", "no");
+            session.setConfig(config);
+
+            ChannelSftp channel;
+            try {
+                session.connect();
+                channel = (ChannelSftp) session.openChannel("sftp");
+                channel.connect();
+            } catch (JSchException e) {
+                throw new HasException("Failed to set the session: " + e.getMessage());
+            }
+            try {
+                String path = "";
+                String[] paths = pathToDeploy.split("/");
+                for (int j = 1; j < paths.length; j++) {
+                    path = path + "/" + paths[i];
+
+                    try {
+                        channel.cd(path);
+                    } catch (SftpException e) {
+                        if (e.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+                            channel.mkdir(path);
+                        } else {
+                            throw new HasException(e.getMessage());
+                        }
+                    }
+                }
+            } catch (SftpException e) {
+                throw new HasException("Failed to mkdir path: " + e.getMessage());
+            }
+
+            for (File keytab : keytabs) {
+                // Send the keytab to remote
+                try {
+                    channel.put(keytab.getAbsolutePath(), pathToDeploy + keytab.getName());
+                } catch (SftpException e) {
+                    throw new HasException("Failed to send the keytab file: " + keytab.getName());
+                }
+            }
+            channel.disconnect();
+        }
+    }
+}
+
+
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/AddPrincipalsCommand.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/AddPrincipalsCommand.java
new file mode 100644
index 0000000..d9db07c
--- /dev/null
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/AddPrincipalsCommand.java
@@ -0,0 +1,75 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.tool.admin.local.cmd;
+
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHadmin;
+import org.codehaus.jettison.json.JSONArray;
+import org.codehaus.jettison.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+
+public class AddPrincipalsCommand extends HadminCommand {
+
+    private static final String USAGE = "\nUsage: create_principals [hostRoles-file]\n"
+            + "\t'hostRoles-file' is a file with a hostRoles json string like:\n"
+            + "\t\t{HOSTS: [ {\"name\":\"host1\",\"hostRoles\":\"HDFS\"}, "
+            + "{\"name\":\"host2\",\"hostRoles\":\"HDFS,HBASE\"} ] }\n"
+            + "\tExample:\n"
+            + "\t\tcreate_principals hostroles.txt\n";
+
+    public AddPrincipalsCommand(LocalHadmin hadmin) {
+        super(hadmin);
+    }
+
+    @Override
+    public void execute(String[] items) throws HasException {
+        if (items.length != 2) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        File hostRoles = new File(items[1]);
+        if (!hostRoles.exists()) {
+            throw new HasException("HostRoles file is not exists.");
+        }
+        try {
+            BufferedReader reader = new BufferedReader(new FileReader(hostRoles));
+            StringBuilder sb = new StringBuilder();
+            String tempString;
+            while ((tempString = reader.readLine()) != null) {
+                sb.append(tempString);
+            }
+            JSONArray hostArray = new JSONObject(sb.toString()).optJSONArray("HOSTS");
+            for (int i = 0; i < hostArray.length(); i++) {
+                JSONObject host = (JSONObject) hostArray.get(i);
+                String[] roles = host.getString("hostRoles").split(",");
+                for (String role : roles) {
+                    System.out.println(getHadmin().addPrincByRole(host.getString("name"),
+                            role.toUpperCase()));
+                }
+            }
+        } catch (Exception e) {
+            throw new HasException("Failed to execute creating principals, because : " + e.getMessage());
+        }
+    }
+}
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/DeployHTTPSCertsCommand.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/DeployHTTPSCertsCommand.java
new file mode 100644
index 0000000..e49ffa5
--- /dev/null
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/DeployHTTPSCertsCommand.java
@@ -0,0 +1,310 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.tool.admin.local.cmd;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpException;
+import org.apache.commons.text.CharacterPredicates;
+import org.apache.commons.text.RandomStringGenerator;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHadmin;
+import org.apache.kerby.util.IOUtil;
+import org.bouncycastle.x509.X509V1CertificateGenerator;
+
+import javax.security.auth.x500.X500Principal;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * HTTPS certifications deploy tool.
+ */
+public class DeployHTTPSCertsCommand extends HadminCommand {
+    private static final String USAGE
+            = "\nUsage: deploy_certs [Hosts-File] [truststore_file] [truststore_password]"
+            + " [Where-to-Deploy] [SSH-Port] [UserName] [Password]\n"
+            + "\tExample:\n"
+            + "\t\tdeploy_https hosts.txt /etc/has/truststore.jks 123456 /etc/has 22 username password\n";
+
+    public DeployHTTPSCertsCommand(LocalHadmin hadmin) {
+        super(hadmin);
+    }
+
+    private static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(1024);
+        return keyGen.genKeyPair();
+    }
+
+    private static X509Certificate generateCertificate(String args, KeyPair pair)
+            throws CertificateEncodingException, InvalidKeyException, IllegalStateException,
+            NoSuchAlgorithmException, SignatureException {
+
+        Date from = new Date();
+        Date to = new Date(from.getTime() + 90 * 86400000L);
+        BigInteger sn = new BigInteger(64, new SecureRandom());
+        X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+        X500Principal dnName = new X500Principal(args);
+
+        certGen.setSerialNumber(sn);
+        certGen.setIssuerDN(dnName);
+        certGen.setNotBefore(from);
+        certGen.setNotAfter(to);
+        certGen.setSubjectDN(dnName);
+        certGen.setPublicKey(pair.getPublic());
+        certGen.setSignatureAlgorithm("SHA1withRSA");
+
+        return certGen.generate(pair.getPrivate());
+    }
+
+    private static File saveKeyStore(String fileName, KeyStore ks, String password)
+            throws GeneralSecurityException, IOException {
+        File keystoreFile = new File(fileName);
+        if (keystoreFile.exists() && !keystoreFile.delete()) {
+            throw new IOException("Failed to delete original file: " + fileName);
+        }
+        FileOutputStream out = new FileOutputStream(keystoreFile);
+        ks.store(out, password.toCharArray());
+        out.close();
+        return keystoreFile;
+    }
+
+    private File createClientSSLConfig(String trustStorePath, String trustStorePassword,
+                                       String keyStorePassword) throws HasException {
+        String resourcePath = "/ssl-client.conf.template";
+        InputStream templateResource = getClass().getResourceAsStream(resourcePath);
+        File sslConfigFile = new File("ssl-client.conf");
+        try {
+            String content = IOUtil.readInput(templateResource);
+            content = content.replaceAll("_location_", trustStorePath);
+            content = content.replaceAll("_password_", trustStorePassword);
+            content = content.replaceAll("_keyPassword_", keyStorePassword);
+
+            IOUtil.writeFile(content, sslConfigFile);
+            return sslConfigFile;
+        } catch (IOException e) {
+            throw new HasException("Failed to create client ssl configuration file", e);
+        }
+    }
+
+    private final class KeyStoreInfo {
+        KeyStore keyStore;
+        String keyPasswd;
+
+        private KeyStoreInfo(KeyStore keyStore, String keyPasswd) {
+            this.keyStore = keyStore;
+            this.keyPasswd = keyPasswd;
+        }
+
+        private String getKeyPasswd() {
+            return this.keyPasswd;
+        }
+
+        private KeyStore getKeyStore() {
+            return this.keyStore;
+        }
+    }
+
+    @Override
+    public void execute(String[] items) throws HasException {
+
+        if (items.length < 7 || items.length > 8) {
+            System.err.println(USAGE);
+            return;
+        }
+
+        File hostFile = new File(items[1]);
+        if (!hostFile.exists()) {
+            throw new HasException("Host file is not exist.");
+        }
+        String truststoreFile = items[2];
+        String truststoreSecret = items[3];
+        String pathToDeploy = items[4];
+        int port = Integer.valueOf(items[5]);
+        String username = items[6];
+        String password = "";
+        if (items.length == 8) {
+            password = items[7];
+        }
+
+        // Get hosts from host file
+        BufferedReader reader;
+        try {
+            reader = new BufferedReader(new FileReader(hostFile));
+        } catch (FileNotFoundException e) {
+            throw new HasException("The hosts file: " + hostFile
+                + "is not exist. " + e.getMessage());
+        }
+        StringBuilder sb = new StringBuilder();
+        String tempString;
+        try {
+            while ((tempString = reader.readLine()) != null) {
+                sb.append(tempString);
+            }
+        } catch (IOException e1) {
+            throw new HasException("Failed to read file: " + e1.getMessage());
+        }
+        String[] hostArray = sb.toString().replace(" ", "").split(",");
+
+        // Get truststore from truststore file
+        Map<String, KeyStoreInfo> keyStoreInfoMap = new HashMap<>(16);
+        KeyStore trustStore;
+        try {
+            trustStore = KeyStore.getInstance("JKS");
+            FileInputStream in = new FileInputStream(truststoreFile);
+            trustStore.load(in, truststoreSecret.toCharArray());
+        } catch (Exception e2) {
+            throw new HasException("Failed to get truststore from the file: "
+                + truststoreFile, e2);
+        }
+        RandomStringGenerator generator = new RandomStringGenerator.Builder()
+            .withinRange('a', 'z')
+            .filteredBy(CharacterPredicates.LETTERS, CharacterPredicates.DIGITS)
+            .build();
+
+        // Generate keystore map
+        for (String hostname : hostArray) {
+            try {
+                InetAddress inetAddress = InetAddress.getLocalHost();
+                String localHostname = inetAddress.getHostName();
+                if (hostname.equals(localHostname)) {
+                    continue;
+                }
+            } catch (UnknownHostException e3) {
+                throw new HasException("Failed to get local hostname.", e3);
+            }
+
+            KeyStore ks;
+            try {
+                KeyPair cKP = generateKeyPair();
+                String keyPassword = generator.generate(15);
+                X509Certificate cert = generateCertificate("CN=" + hostname + ", O=has", cKP);
+                ks = KeyStore.getInstance("JKS");
+                ks.load(null, null);
+                ks.setKeyEntry(hostname, cKP.getPrivate(), keyPassword.toCharArray(),
+                    new Certificate[]{cert});
+                KeyStoreInfo keyStoreInfo = new KeyStoreInfo(ks, keyPassword);
+                keyStoreInfoMap.put(hostname, keyStoreInfo);
+                trustStore.setCertificateEntry(hostname, cert);
+            } catch (Exception e4) {
+                throw new HasException("Failed to generate keystore.", e4);
+            }
+        }
+
+        File finalTrustStoreFile;
+        try {
+            finalTrustStoreFile = saveKeyStore(truststoreFile, trustStore, password);
+        } catch (Exception e5) {
+            throw new HasException("Failed to generate trust store files.", e5);
+        }
+
+        // Generate keystore, truststore, ssl config files and transfer them to destination
+        for (String hostname : hostArray) {
+            List<File> files = new ArrayList<>(3);
+            try {
+                KeyStoreInfo keyStoreInfo = keyStoreInfoMap.get(hostname);
+                File file = saveKeyStore(hostname + "_keystore.jks",
+                    keyStoreInfo.getKeyStore(), keyStoreInfo.getKeyPasswd());
+                files.add(file);
+                files.add(finalTrustStoreFile);
+                files.add(createClientSSLConfig(pathToDeploy + "/truststore.jks",
+                    truststoreSecret, keyStoreInfo.getKeyPasswd()));
+            } catch (Exception e6) {
+                throw new HasException("Failed to generate key store files.", e6);
+            }
+
+            JSch jsch = new JSch();
+            Session session;
+            try {
+                session = jsch.getSession(username, hostname, port);
+            } catch (JSchException e7) {
+                throw new HasException(e7.getMessage());
+            }
+            session.setPassword(password);
+
+            java.util.Properties config = new java.util.Properties();
+            config.put("StrictHostKeyChecking", "no");
+            session.setConfig(config);
+
+            ChannelSftp channel;
+            try {
+                session.connect();
+                channel = (ChannelSftp) session.openChannel("sftp");
+                channel.connect();
+            } catch (JSchException e8) {
+                throw new HasException("Failed to set the session: " + e8.getMessage());
+            }
+            try {
+                String path = "";
+                String[] paths = pathToDeploy.split("/");
+                for (int i = 1; i < paths.length; i++) {
+                    path = path + "/" + paths[i];
+                    try {
+                        channel.cd(path);
+                    } catch (SftpException e9) {
+                        if (e9.id == ChannelSftp.SSH_FX_NO_SUCH_FILE) {
+                            channel.mkdir(path);
+                        } else {
+                            throw new HasException(e9.getMessage());
+                        }
+                    }
+                }
+            } catch (SftpException e10) {
+                throw new HasException("Failed to mkdir path: " + e10);
+            }
+
+            for (File file : files) {
+                try {
+                    channel.put(file.getAbsolutePath(), file.getName());
+                } catch (SftpException e10) {
+                    throw new HasException("Failed to send the https cert files.", e10);
+                }
+            }
+            channel.disconnect();
+        }
+    }
+}
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/DisableConfigureCommand.java
similarity index 65%
copy from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java
copy to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/DisableConfigureCommand.java
index 6b72db3..a499076 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/DisableConfigureCommand.java
@@ -17,28 +17,24 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.local.cmd;
 
-import org.apache.kerby.has.client.HasAuthAdminClient;
-import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHadmin;
 
-/**
- * Remote add principal cmd
- */
-public class EnableConfRemoteCmd extends AdminRemoteCmd {
+public class DisableConfigureCommand extends HadminCommand {
 
     public static final String USAGE = "Usage: enable_configure\n"
             + "\tExample:\n"
             + "\t\tenable\n";
 
-    public EnableConfRemoteCmd(HasAuthAdminClient authHadmin) {
-        super(authHadmin);
+    public DisableConfigureCommand(LocalHadmin hadmin) {
+        super(hadmin);
     }
 
     @Override
-    public void execute(String[] items) throws KrbException {
-
-        HasAuthAdminClient client = getAuthAdminClient();
-        client.setEnableOfConf("true");
+    public void execute(String[] items) throws HasException {
+        getHadmin().setEnableOfConf("false");
+        System.out.println("Set conf disable.");
     }
 }
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/EnableConfigureCommand.java
similarity index 65%
copy from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java
copy to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/EnableConfigureCommand.java
index 6b72db3..404e3f5 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/EnableConfigureCommand.java
@@ -17,28 +17,24 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.local.cmd;
 
-import org.apache.kerby.has.client.HasAuthAdminClient;
-import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHadmin;
 
-/**
- * Remote add principal cmd
- */
-public class EnableConfRemoteCmd extends AdminRemoteCmd {
+public class EnableConfigureCommand extends HadminCommand {
 
     public static final String USAGE = "Usage: enable_configure\n"
             + "\tExample:\n"
             + "\t\tenable\n";
 
-    public EnableConfRemoteCmd(HasAuthAdminClient authHadmin) {
-        super(authHadmin);
+    public EnableConfigureCommand(LocalHadmin hadmin) {
+        super(hadmin);
     }
 
     @Override
-    public void execute(String[] items) throws KrbException {
-
-        HasAuthAdminClient client = getAuthAdminClient();
-        client.setEnableOfConf("true");
+    public void execute(String[] items) throws HasException {
+        getHadmin().setEnableOfConf("true");
+        System.out.println("Set conf enable.");
     }
 }
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ExportKeytabsRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/ExportKeytabsCommand.java
similarity index 62%
copy from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ExportKeytabsRemoteCmd.java
copy to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/ExportKeytabsCommand.java
index 5d5614d..1892814 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ExportKeytabsRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/ExportKeytabsCommand.java
@@ -17,36 +17,38 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.local.cmd;
 
-import org.apache.kerby.has.client.HasAuthAdminClient;
-import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHadmin;
+import org.apache.kerby.has.server.web.HostRoleType;
 
-public class ExportKeytabsRemoteCmd extends AdminRemoteCmd {
+public class ExportKeytabsCommand extends HadminCommand {
     private static final String USAGE = "\nUsage: export_keytabs <host> [role]\n"
             + "\tExample:\n"
             + "\t\texport_keytabs host1 HDFS\n";
 
-    public ExportKeytabsRemoteCmd(HasAuthAdminClient authHadmin) {
-        super(authHadmin);
+    public ExportKeytabsCommand(LocalHadmin hadmin) {
+        super(hadmin);
     }
 
     @Override
-    public void execute(String[] items) throws KrbException {
-        //TODO add save path option
-        //String param = items[0];
+    public void execute(String[] items) throws HasException {
         if (items.length < 2) {
             System.err.println(USAGE);
             return;
         }
-
-        HasAuthAdminClient client = getAuthAdminClient();
-
         String host = items[1];
-        String role = "";
         if (items.length >= 3) {
-            role = items[2];
+            exportKeytab(host, items[2]);
+            return;
         }
-        client.getKeytabByHostAndRole(host, role);
+        for (HostRoleType r : HostRoleType.values()) {
+            exportKeytab(host, r.getName());
+        }
+    }
+
+    public void exportKeytab(String host, String role) throws HasException {
+        getHadmin().getKeytabByHostAndRole(host, role);
     }
 }
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/GetHostRolesCommand.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/GetHostRolesCommand.java
new file mode 100644
index 0000000..b77de5c
--- /dev/null
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/GetHostRolesCommand.java
@@ -0,0 +1,38 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.kerby.kerberos.tool.admin.local.cmd;
+
+import org.apache.kerby.has.server.admin.LocalHadmin;
+
+public class GetHostRolesCommand extends HadminCommand {
+
+    public static final String USAGE = "Usage: get_hostroles\n"
+            + "\tExample:\n"
+            + "\t\tget_hostroles\n";
+
+    public GetHostRolesCommand(LocalHadmin hadmin) {
+        super(hadmin);
+    }
+
+    @Override
+    public void execute(String[] items) {
+        getHadmin().getHostRoles();
+    }
+}
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AdminRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/HadminCommand.java
similarity index 60%
copy from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AdminRemoteCmd.java
copy to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/HadminCommand.java
index b74f69f..aa739e2 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AdminRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/local/cmd/HadminCommand.java
@@ -17,26 +17,26 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.local.cmd;
 
-import org.apache.kerby.has.client.HasAuthAdminClient;
-import org.apache.kerby.kerberos.kerb.KrbException;
+import org.apache.kerby.has.common.HasException;
+import org.apache.kerby.has.server.admin.LocalHadmin;
 
-public abstract class AdminRemoteCmd {
+public abstract class HadminCommand {
 
-    private HasAuthAdminClient client;
+    private LocalHadmin hadmin;
 
-    public AdminRemoteCmd(HasAuthAdminClient authHadminClient) {
-        this.client = authHadminClient;
+    public HadminCommand(LocalHadmin hadmin) {
+        this.hadmin = hadmin;
     }
 
-    protected HasAuthAdminClient getAuthAdminClient() {
-        return client;
+    protected LocalHadmin getHadmin() {
+        return hadmin;
     }
 
     /**
-     * Execute the admin cmd.
-     * @param items Input cmd to execute
+     * Execute the hadmin cmd.
+     * @param input Input cmd to execute
      */
-    public abstract void execute(String[] items) throws KrbException;
+    public abstract void execute(String[] input) throws HasException;
 }
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/AdminRemoteTool.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/AdminRemoteTool.java
similarity index 88%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/AdminRemoteTool.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/AdminRemoteTool.java
index c9330b6..aa6beca 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/AdminRemoteTool.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/AdminRemoteTool.java
@@ -17,23 +17,23 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin;
+package org.apache.kerby.kerberos.tool.admin.remote;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.has.common.HasConfig;
 import org.apache.kerby.has.common.HasException;
 import org.apache.kerby.has.common.util.HasUtil;
 import org.apache.kerby.kerberos.kerb.KrbException;
-import org.apache.kerby.kerberos.tool.admin.cmd.AddPrincipalRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.AddPrincipalsRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.AdminRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.DeletePrincipalRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.DisableConfRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.EnableConfRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.ExportKeytabsRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.GetHostRolesRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.ListPrincipalsRemoteCmd;
-import org.apache.kerby.kerberos.tool.admin.cmd.RenamePrincipalRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.AddPrincipalRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.AddPrincipalsRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.AdminRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.DeletePrincipalRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.DisableConfRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.EnableConfRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.ExportKeytabsRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.GetHostRolesRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.ListPrincipalsRemoteCmd;
+import org.apache.kerby.kerberos.tool.admin.remote.cmd.RenamePrincipalRemoteCmd;
 import org.apache.kerby.util.OSUtil;
 import org.jline.reader.Completer;
 import org.jline.reader.EndOfFileException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AddPrincipalRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AddPrincipalRemoteCmd.java
similarity index 97%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AddPrincipalRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AddPrincipalRemoteCmd.java
index 6cbe325..dce0ed7 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AddPrincipalRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AddPrincipalRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AddPrincipalsRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AddPrincipalsRemoteCmd.java
similarity index 97%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AddPrincipalsRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AddPrincipalsRemoteCmd.java
index fd6cfa6..16ebc97 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AddPrincipalsRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AddPrincipalsRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AdminRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AdminRemoteCmd.java
similarity index 95%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AdminRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AdminRemoteCmd.java
index b74f69f..0466cbe 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/AdminRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/AdminRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/DeletePrincipalRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/DeletePrincipalRemoteCmd.java
similarity index 97%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/DeletePrincipalRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/DeletePrincipalRemoteCmd.java
index fee5b0d..dbd3cd2 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/DeletePrincipalRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/DeletePrincipalRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/DisableConfRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/DisableConfRemoteCmd.java
similarity index 95%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/DisableConfRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/DisableConfRemoteCmd.java
index 9a6d6a3..07b5055 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/DisableConfRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/DisableConfRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/EnableConfRemoteCmd.java
similarity index 95%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/EnableConfRemoteCmd.java
index 6b72db3..4bf0080 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/EnableConfRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/EnableConfRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ExportKeytabsRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/ExportKeytabsRemoteCmd.java
similarity index 96%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ExportKeytabsRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/ExportKeytabsRemoteCmd.java
index 5d5614d..c16c034 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ExportKeytabsRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/ExportKeytabsRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/GetHostRolesRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/GetHostRolesRemoteCmd.java
similarity index 97%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/GetHostRolesRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/GetHostRolesRemoteCmd.java
index cc01e63..e511a51 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/GetHostRolesRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/GetHostRolesRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ListPrincipalsRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/ListPrincipalsRemoteCmd.java
similarity index 97%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ListPrincipalsRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/ListPrincipalsRemoteCmd.java
index 470ca33..b5d855a 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/ListPrincipalsRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/ListPrincipalsRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/RenamePrincipalRemoteCmd.java b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/RenamePrincipalRemoteCmd.java
similarity index 97%
rename from kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/RenamePrincipalRemoteCmd.java
rename to kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/RenamePrincipalRemoteCmd.java
index 0be563e..5684df2 100644
--- a/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/cmd/RenamePrincipalRemoteCmd.java
+++ b/kerby-tool/has-tool/src/main/java/org/apache/kerby/kerberos/tool/admin/remote/cmd/RenamePrincipalRemoteCmd.java
@@ -17,7 +17,7 @@
  *  under the License.
  *
  */
-package org.apache.kerby.kerberos.tool.admin.cmd;
+package org.apache.kerby.kerberos.tool.admin.remote.cmd;
 
 import org.apache.kerby.has.client.HasAuthAdminClient;
 import org.apache.kerby.kerberos.kerb.KrbException;
diff --git a/pom.xml b/pom.xml
index e0d8a2d..c9ca6e6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,6 +71,7 @@
     <commons-text.version>1.1</commons-text.version>
     <commons-dbutils.version>1.6</commons-dbutils.version>
     <drizzle-jdbc.version>1.4</drizzle-jdbc.version>
+    <jsch.version>0.1.54</jsch.version>
   </properties>
 
   <prerequisites>
@@ -82,13 +83,13 @@
     <module>kerby-pkix</module>
     <module>kerby-kerb</module>
     <module>kerby-kdc</module>
-    <module>kerby-tool</module>
     <module>kerby-kdc-test</module>
     <module>kerby-backend</module>
-    <module>kerby-dist</module>
     <module>benchmark</module>
     <module>kerby-provider</module>
     <module>has-project</module>
+    <module>kerby-tool</module>
+    <module>kerby-dist</module>
   </modules>
 
   <dependencyManagement>