You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ao...@apache.org on 2015/10/22 15:09:02 UTC

ambari git commit: https://issues.apache.org/jira/browse/AMBARI-13450. AMBARI-13450 Bootstrap Cluster via different SSH Port Number (Selim Ozcan via aonishuk)

Repository: ambari
Updated Branches:
  refs/heads/trunk 565dc0de8 -> 39c04ac9b


https://issues.apache.org/jira/browse/AMBARI-13450. AMBARI-13450 Bootstrap Cluster via different SSH Port Number (Selim Ozcan via aonishuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/39c04ac9
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/39c04ac9
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/39c04ac9

Branch: refs/heads/trunk
Commit: 39c04ac9b44f4eb1035b378337d745d77ee83d53
Parents: 565dc0d
Author: Andrew Onishuk <ao...@hortonworks.com>
Authored: Thu Oct 22 16:08:48 2015 +0300
Committer: Andrew Onishuk <ao...@hortonworks.com>
Committed: Thu Oct 22 16:08:48 2015 +0300

----------------------------------------------------------------------
 .../ambari/server/bootstrap/BSRunner.java       |  28 +++--
 .../ambari/server/bootstrap/SshHostInfo.java    |   7 ++
 ambari-server/src/main/python/bootstrap.py      |  66 ++++++------
 ambari-server/src/test/python/TestBootstrap.py  | 102 +++++++++----------
 ambari-web/app/controllers/wizard.js            |   2 +
 .../app/controllers/wizard/step2_controller.js  |  25 ++++-
 .../app/controllers/wizard/step3_controller.js  |   2 +
 ambari-web/app/messages.js                      |   3 +
 ambari-web/app/styles/application.less          |   4 +
 ambari-web/app/templates/wizard/step2.hbs       |  12 +++
 .../test/controllers/wizard/step2_test.js       |  45 ++++++++
 .../test/controllers/wizard/step3_test.js       |   4 +
 12 files changed, 205 insertions(+), 95 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSRunner.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSRunner.java b/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSRunner.java
index 0a55131..44faa4f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSRunner.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/BSRunner.java
@@ -41,6 +41,7 @@ class BSRunner extends Thread {
   private static Log LOG = LogFactory.getLog(BSRunner.class);
 
   private static final String DEFAULT_USER = "root";
+  private static final String DEFAULT_SSHPORT = "22";
 
   private  boolean finished = false;
   private SshHostInfo sshHostInfo;
@@ -164,7 +165,13 @@ class BSRunner extends Thread {
     if (user == null || user.isEmpty()) {
       user = DEFAULT_USER;
     }
-    String command[] = new String[12];
+
+    String sshPort = sshHostInfo.getSshPort();
+    if(sshPort == null || sshPort.isEmpty()){
+       sshPort = DEFAULT_SSHPORT;
+    }
+
+    String command[] = new String[13];
     BSStat stat = BSStat.RUNNING;
     String scriptlog = "";
     try {
@@ -194,14 +201,15 @@ class BSRunner extends Thread {
       command[1] = hostString;
       command[2] = this.requestIdDir.toString();
       command[3] = user;
-      command[4] = this.sshKeyFile.toString();
-      command[5] = this.agentSetupScript.toString();
-      command[6] = this.ambariHostname;
-      command[7] = this.clusterOsFamily;
-      command[8] = this.projectVersion;
-      command[9] = this.serverPort+"";
-      command[10] = userRunAs;
-      command[11] = (this.passwordFile==null) ? "null" : this.passwordFile.toString();
+      command[4] = sshPort;
+      command[5] = this.sshKeyFile.toString();
+      command[6] = this.agentSetupScript.toString();
+      command[7] = this.ambariHostname;
+      command[8] = this.clusterOsFamily;
+      command[9] = this.projectVersion;
+      command[10] = this.serverPort+"";
+      command[11] = userRunAs;
+      command[12] = (this.passwordFile==null) ? "null" : this.passwordFile.toString();
 
       Map<String, String> envVariables = new HashMap<String, String>();
 
@@ -218,7 +226,7 @@ class BSRunner extends Thread {
       }
 
       LOG.info("Host= " + hostString + " bs=" + this.bsScript + " requestDir=" +
-          requestIdDir + " user=" + user + " keyfile=" + this.sshKeyFile +
+          requestIdDir + " user=" + user + " sshPort=" + sshPort + " keyfile=" + this.sshKeyFile +
           " passwordFile " + this.passwordFile + " server=" + this.ambariHostname +
           " version=" + projectVersion + " serverPort=" + this.serverPort + " userRunAs=" + userRunAs);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/SshHostInfo.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/SshHostInfo.java b/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/SshHostInfo.java
index 822e972..9a7490f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/SshHostInfo.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/bootstrap/SshHostInfo.java
@@ -49,6 +49,9 @@ public class SshHostInfo {
   private String user;
 
   @XmlElement
+  private String sshPort;
+
+  @XmlElement
   private String password;
   
   @XmlElement
@@ -86,6 +89,10 @@ public class SshHostInfo {
     this.user = user;
   }
 
+  public String getSshPort(){ return sshPort; }
+
+  public void setSshPort(String sshPort){ this.sshPort = sshPort; }
+
   public String getPassword() {
     return password;
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-server/src/main/python/bootstrap.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/python/bootstrap.py b/ambari-server/src/main/python/bootstrap.py
index 3eba75c..75bb26a 100755
--- a/ambari-server/src/main/python/bootstrap.py
+++ b/ambari-server/src/main/python/bootstrap.py
@@ -74,8 +74,9 @@ class HostLog:
 class SCP:
   """ SCP implementation that is thread based. The status can be returned using
    status val """
-  def __init__(self, user, sshkey_file, host, inputFile, remote, bootdir, host_log):
+  def __init__(self, user, sshPort, sshkey_file, host, inputFile, remote, bootdir, host_log):
     self.user = user
+    self.sshPort = sshPort
     self.sshkey_file = sshkey_file
     self.host = host
     self.inputFile = inputFile
@@ -90,7 +91,7 @@ class SCP:
                   "-r",
                   "-o", "ConnectTimeout=60",
                   "-o", "BatchMode=yes",
-                  "-o", "StrictHostKeyChecking=no",
+                  "-o", "StrictHostKeyChecking=no", "-P", self.sshPort,
                   "-i", self.sshkey_file, self.inputFile, self.user + "@" +
                                                          self.host + ":" + self.remote]
     if DEBUG:
@@ -111,8 +112,9 @@ class SCP:
 
 class SSH:
   """ Ssh implementation of this """
-  def __init__(self, user, sshkey_file, host, command, bootdir, host_log, errorMessage = None):
+  def __init__(self, user, sshPort, sshkey_file, host, command, bootdir, host_log, errorMessage = None):
     self.user = user
+    self.sshPort = sshPort
     self.sshkey_file = sshkey_file
     self.host = host
     self.command = command
@@ -128,7 +130,7 @@ class SSH:
                   "-o", "StrictHostKeyChecking=no",
                   "-o", "BatchMode=yes",
                   "-tt", # Should prevent "tput: No value for $TERM and no -T specified" warning
-                  "-i", self.sshkey_file,
+                  "-i", self.sshkey_file, "-p", self.sshPort,
                   self.user + "@" + self.host, self.command]
     if DEBUG:
       self.host_log.write("Running ssh command " + ' '.join(sshcommand))
@@ -454,7 +456,7 @@ class BootstrapDefault(Bootstrap):
     params = self.shared_state
     self.host_log.write("==========================\n")
     self.host_log.write("Copying OS type check script...")
-    scp = SCP(params.user, params.sshkey_file, self.host, fileToCopy,
+    scp = SCP(params.user, params.sshPort, params.sshkey_file, self.host, fileToCopy,
               target, params.bootdir, self.host_log)
     result = scp.run()
     self.host_log.write("\n")
@@ -467,7 +469,7 @@ class BootstrapDefault(Bootstrap):
     params = self.shared_state
     self.host_log.write("==========================\n")
     self.host_log.write("Copying common functions script...")
-    scp = SCP(params.user, params.sshkey_file, self.host, fileToCopy,
+    scp = SCP(params.user, params.sshPort, params.sshkey_file, self.host, fileToCopy,
               target, params.bootdir, self.host_log)
     result = scp.run()
     self.host_log.write("\n")
@@ -507,7 +509,7 @@ class BootstrapDefault(Bootstrap):
     if (os.path.exists(fileToCopy)):
       self.host_log.write("==========================\n")
       self.host_log.write("Copying repo file to 'tmp' folder...")
-      scp = SCP(params.user, params.sshkey_file, self.host, fileToCopy,
+      scp = SCP(params.user, params.sshPort, params.sshkey_file, self.host, fileToCopy,
                 target, params.bootdir, self.host_log)
       retcode1 = scp.run()
       self.host_log.write("\n")
@@ -517,7 +519,7 @@ class BootstrapDefault(Bootstrap):
       self.host_log.write("Moving file to repo dir...")
       targetDir = self.getRepoDir()
       command = self.getMoveRepoFileCommand(targetDir)
-      ssh = SSH(params.user, params.sshkey_file, self.host, command,
+      ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
                 params.bootdir, self.host_log)
       retcode2 = ssh.run()
       self.host_log.write("\n")
@@ -526,7 +528,7 @@ class BootstrapDefault(Bootstrap):
       self.host_log.write("==========================\n")
       self.host_log.write("Changing permissions for ambari.repo...")
       command = self.getRepoFileChmodCommand()
-      ssh = SSH(params.user, params.sshkey_file, self.host, command,
+      ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
                 params.bootdir, self.host_log)
       retcode4 = ssh.run()
       self.host_log.write("\n")
@@ -536,7 +538,7 @@ class BootstrapDefault(Bootstrap):
         self.host_log.write("==========================\n")
         self.host_log.write("Update apt cache of repository...")
         command = self.getAptUpdateCommand()
-        ssh = SSH(params.user, params.sshkey_file, self.host, command,
+        ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
                   params.bootdir, self.host_log)
         retcode2 = ssh.run()
         self.host_log.write("\n")
@@ -554,7 +556,7 @@ class BootstrapDefault(Bootstrap):
     self.host_log.write("Copying setup script file...")
     fileToCopy = params.setup_agent_file
     target = self.getRemoteName(self.SETUP_SCRIPT_FILENAME)
-    scp = SCP(params.user, params.sshkey_file, self.host, fileToCopy,
+    scp = SCP(params.user, params.sshPort, params.sshkey_file, self.host, fileToCopy,
               target, params.bootdir, self.host_log)
     retcode3 = scp.run()
     self.host_log.write("\n")
@@ -600,7 +602,7 @@ class BootstrapDefault(Bootstrap):
               (self.getOsCheckScriptRemoteLocation(),
                PYTHON_ENV, self.getOsCheckScriptRemoteLocation(), params.cluster_os_type)
 
-    ssh = SSH(params.user, params.sshkey_file, self.host, command,
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
               params.bootdir, self.host_log)
     retcode = ssh.run()
     self.host_log.write("\n")
@@ -615,7 +617,7 @@ class BootstrapDefault(Bootstrap):
       command = "dpkg --get-selections|grep -e '^sudo\s*install'"
     else:
       command = "rpm -qa | grep -e '^sudo\-'"
-    ssh = SSH(params.user, params.sshkey_file, self.host, command,
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
               params.bootdir, self.host_log,
               errorMessage="Error: Sudo command is not available. "
                            "Please install the sudo command.")
@@ -627,7 +629,7 @@ class BootstrapDefault(Bootstrap):
     # Copy the password file
     self.host_log.write("Copying password file to 'tmp' folder...")
     params = self.shared_state
-    scp = SCP(params.user, params.sshkey_file, self.host, params.password_file,
+    scp = SCP(params.user, params.sshPort, params.sshkey_file, self.host, params.password_file,
               self.getPasswordFile(), params.bootdir, self.host_log)
     retcode1 = scp.run()
 
@@ -636,7 +638,7 @@ class BootstrapDefault(Bootstrap):
     # Change password file mode to 600
     self.host_log.write("Changing password file mode...")
     command = "chmod 600 " + self.getPasswordFile()
-    ssh = SSH(params.user, params.sshkey_file, self.host, command,
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
               params.bootdir, self.host_log)
     retcode2 = ssh.run()
 
@@ -648,7 +650,7 @@ class BootstrapDefault(Bootstrap):
     self.host_log.write("Changing password file mode...")
     params = self.shared_state
     command = "chmod 600 " + self.getPasswordFile()
-    ssh = SSH(params.user, params.sshkey_file, self.host, command,
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
               params.bootdir, self.host_log)
     retcode = ssh.run()
     self.host_log.write("Change password file mode on host finished")
@@ -659,7 +661,7 @@ class BootstrapDefault(Bootstrap):
     self.host_log.write("Deleting password file...")
     params = self.shared_state
     command = "rm " + self.getPasswordFile()
-    ssh = SSH(params.user, params.sshkey_file, self.host, command,
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
               params.bootdir, self.host_log)
     retcode = ssh.run()
     self.host_log.write("Deleting password file finished")
@@ -675,7 +677,7 @@ class BootstrapDefault(Bootstrap):
     command = "sudo mkdir -p {0} ; sudo chown -R {1} {0} ; sudo chmod 755 {3} ; sudo chmod 755 {2} ; sudo chmod 777 {0}".format(
       self.TEMP_FOLDER, quote_bash_args(params.user), DEFAULT_AGENT_DATA_FOLDER, DEFAULT_AGENT_LIB_FOLDER)
 
-    ssh = SSH(params.user, params.sshkey_file, self.host, command,
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
               params.bootdir, self.host_log)
     retcode = ssh.run()
     self.host_log.write("\n")
@@ -692,7 +694,7 @@ class BootstrapDefault(Bootstrap):
     self.host_log.write("==========================\n")
     self.host_log.write("Running setup agent script...")
     command = self.getRunSetupCommand(self.host)
-    ssh = SSH(params.user, params.sshkey_file, self.host, command,
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, self.host, command,
               params.bootdir, self.host_log)
     retcode = ssh.run()
     self.host_log.write("\n")
@@ -791,11 +793,12 @@ class PBootstrap:
 
 
 class SharedState:
-  def __init__(self, user, sshkey_file, script_dir, boottmpdir, setup_agent_file,
+  def __init__(self, user, sshPort, sshkey_file, script_dir, boottmpdir, setup_agent_file,
                ambari_server, cluster_os_type, ambari_version, server_port,
                user_run_as, password_file = None):
     self.hostlist_to_remove_password_file = None
     self.user = user
+    self.sshPort = sshPort
     self.sshkey_file = sshkey_file
     self.bootdir = boottmpdir
     self.script_dir = script_dir
@@ -817,7 +820,7 @@ def main(argv=None):
   onlyargs = argv[1:]
   if len(onlyargs) < 3:
     sys.stderr.write("Usage: <comma separated hosts> "
-                     "<tmpdir for storage> <user> <sshkey_file> <agent setup script>"
+                     "<tmpdir for storage> <user> <sshPort> <sshkey_file> <agent setup script>"
                      " <ambari-server name> <cluster os type> <ambari version> <ambari port> <user_run_as> <passwordFile>\n")
     sys.exit(2)
     pass
@@ -827,14 +830,15 @@ def main(argv=None):
   hostList = onlyargs[0].split(",")
   bootdir =  onlyargs[1]
   user = onlyargs[2]
-  sshkey_file = onlyargs[3]
-  setupAgentFile = onlyargs[4]
-  ambariServer = onlyargs[5]
-  cluster_os_type = onlyargs[6]
-  ambariVersion = onlyargs[7]
-  server_port = onlyargs[8]
-  user_run_as = onlyargs[9]
-  passwordFile = onlyargs[10]
+  sshPort = onlyargs[3]
+  sshkey_file = onlyargs[4]
+  setupAgentFile = onlyargs[5]
+  ambariServer = onlyargs[6]
+  cluster_os_type = onlyargs[7]
+  ambariVersion = onlyargs[8]
+  server_port = onlyargs[9]
+  user_run_as = onlyargs[10]
+  passwordFile = onlyargs[11]
 
   if not OSCheck.is_windows_family():
     # ssh doesn't like open files
@@ -845,10 +849,10 @@ def main(argv=None):
 
   logging.info("BootStrapping hosts " + pprint.pformat(hostList) +
                " using " + scriptDir + " cluster primary OS: " + cluster_os_type +
-               " with user '" + user + "' sshKey File " + sshkey_file + " password File " + passwordFile +\
+               " with user '" + user + "'with ssh Port '" + sshPort + "' sshKey File " + sshkey_file + " password File " + passwordFile +\
                " using tmp dir " + bootdir + " ambari: " + ambariServer +"; server_port: " + server_port +\
                "; ambari version: " + ambariVersion+"; user_run_as: " + user_run_as)
-  sharedState = SharedState(user, sshkey_file, scriptDir, bootdir, setupAgentFile,
+  sharedState = SharedState(user, sshPort, sshkey_file, scriptDir, bootdir, setupAgentFile,
                        ambariServer, cluster_os_type, ambariVersion,
                        server_port, user_run_as, passwordFile)
   pbootstrap = PBootstrap(hostList, sharedState)

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-server/src/test/python/TestBootstrap.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/TestBootstrap.py b/ambari-server/src/test/python/TestBootstrap.py
index e143a68..b60c35d 100644
--- a/ambari-server/src/test/python/TestBootstrap.py
+++ b/ambari-server/src/test/python/TestBootstrap.py
@@ -43,7 +43,7 @@ class TestBootstrap(TestCase):
 
 
   def test_getRemoteName(self):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                       "setupAgentFile", "ambariServer", "centos6", None, "8440", "root")
     res = bootstrap_obj = Bootstrap("hostname", shared_state)
     utime1 = 1234
@@ -65,7 +65,7 @@ class TestBootstrap(TestCase):
   # TODO: test_return_error_message_for_missing_sudo_package
 
   def test_getAmbariPort(self):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -82,11 +82,11 @@ class TestBootstrap(TestCase):
   @patch("os.path.dirname")
   @patch("os.path.realpath")
   def test_bootstrap_main(self, dirname_mock, realpath_mock, run_mock, exit_mock, stderr_mock, subprocess_Popen_mock):
-    bootstrap.main(["bootstrap.py", "hostname,hostname2", "/tmp/bootstrap", "root", "sshkey_file", "setupAgent.py", "ambariServer", \
+    bootstrap.main(["bootstrap.py", "hostname,hostname2", "/tmp/bootstrap", "root", "123", "sshkey_file", "setupAgent.py", "ambariServer", \
                     "centos6", "1.1.1", "8440", "root", "passwordfile"])
     self.assertTrue(run_mock.called)
     run_mock.reset_mock()
-    bootstrap.main(["bootstrap.py", "hostname,hostname2", "/tmp/bootstrap", "root", "sshkey_file", "setupAgent.py", "ambariServer", \
+    bootstrap.main(["bootstrap.py", "hostname,hostname2", "/tmp/bootstrap", "root", "123", "sshkey_file", "setupAgent.py", "ambariServer", \
                     "centos6", "1.1.1", "8440", "root", None])
     self.assertTrue(run_mock.called)
     run_mock.reset_mock()
@@ -104,7 +104,7 @@ class TestBootstrap(TestCase):
 
   @patch("os.environ")
   def test_getRunSetupWithPasswordCommand(self, environ_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     environ_mock.__getitem__.return_value = "TEST_PASSPHRASE"
@@ -118,7 +118,7 @@ class TestBootstrap(TestCase):
 
 
   def test_generateRandomFileName(self):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -129,7 +129,7 @@ class TestBootstrap(TestCase):
   @patch.object(OSCheck, "is_redhat_family")
   @patch.object(OSCheck, "is_suse_family")
   def test_getRepoDir(self, is_suse_family, is_redhat_family):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -145,7 +145,7 @@ class TestBootstrap(TestCase):
     self.assertEquals(res, "/etc/yum.repos.d")
 
   def test_getSetupScript(self):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -155,7 +155,7 @@ class TestBootstrap(TestCase):
   def test_run_setup_agent_command_ends_with_project_version(self):
     os.environ[AMBARI_PASSPHRASE_VAR_NAME] = ""
     version = "1.1.1"
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                version, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -166,7 +166,7 @@ class TestBootstrap(TestCase):
   def test_agent_setup_command_without_project_version(self):
     os.environ[AMBARI_PASSPHRASE_VAR_NAME] = ""
     version = None
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                version, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -207,7 +207,7 @@ class TestBootstrap(TestCase):
 
   @patch("subprocess.Popen")
   def test_SCP(self, popenMock):
-    params = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    params = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                   "setupAgentFile", "ambariServer", "centos6",
                                   "1.2.1", "8440", "root")
     host_log_mock = MagicMock()
@@ -216,7 +216,7 @@ class TestBootstrap(TestCase):
       log['text'] = log['text'] + text
 
     host_log_mock.write.side_effect = write_side_effect
-    scp = SCP(params.user, params.sshkey_file, "dummy-host", "src/file",
+    scp = SCP(params.user, params.sshPort, params.sshkey_file, "dummy-host", "src/file",
               "dst/file", params.bootdir, host_log_mock)
     log_sample = "log_sample"
     error_sample = "error_sample"
@@ -233,7 +233,7 @@ class TestBootstrap(TestCase):
     self.assertTrue(error_sample in log['text'])
     command_str = str(popenMock.call_args[0][0])
     self.assertEquals(command_str, "['scp', '-r', '-o', 'ConnectTimeout=60', '-o', "
-        "'BatchMode=yes', '-o', 'StrictHostKeyChecking=no', '-i', 'sshkey_file',"
+        "'BatchMode=yes', '-o', 'StrictHostKeyChecking=no', '-P', '123', '-i', 'sshkey_file',"
         " 'src/file', 'root@dummy-host:dst/file']")
     self.assertEqual(retcode["exitstatus"], 0)
 
@@ -250,7 +250,7 @@ class TestBootstrap(TestCase):
 
   @patch("subprocess.Popen")
   def test_SSH(self, popenMock):
-    params = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    params = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                   "setupAgentFile", "ambariServer", "centos6",
                                   "1.2.1", "8440", "root")
     host_log_mock = MagicMock()
@@ -259,7 +259,7 @@ class TestBootstrap(TestCase):
       log['text'] = log['text'] + text
 
     host_log_mock.write.side_effect = write_side_effect
-    ssh = SSH(params.user, params.sshkey_file, "dummy-host", "dummy-command",
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, "dummy-host", "dummy-command",
               params.bootdir, host_log_mock)
     log_sample = "log_sample"
     error_sample = "error_sample"
@@ -277,7 +277,7 @@ class TestBootstrap(TestCase):
     command_str = str(popenMock.call_args[0][0])
     self.assertEquals(command_str, "['ssh', '-o', 'ConnectTimeOut=60', '-o', "
             "'StrictHostKeyChecking=no', '-o', 'BatchMode=yes', '-tt', '-i', "
-            "'sshkey_file', 'root@dummy-host', 'dummy-command']")
+            "'sshkey_file', '-p', '123', 'root@dummy-host', 'dummy-command']")
     self.assertEqual(retcode["exitstatus"], 0)
 
     log['text'] = ""
@@ -295,7 +295,7 @@ class TestBootstrap(TestCase):
     process.returncode = 1
 
     dummy_error_message = "dummy_error_message"
-    ssh = SSH(params.user, params.sshkey_file, "dummy-host", "dummy-command",
+    ssh = SSH(params.user, params.sshPort, params.sshkey_file, "dummy-host", "dummy-command",
               params.bootdir, host_log_mock, errorMessage= dummy_error_message)
     retcode = ssh.run()
 
@@ -306,7 +306,7 @@ class TestBootstrap(TestCase):
 
 
   def test_getOsCheckScript(self):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -316,7 +316,7 @@ class TestBootstrap(TestCase):
 
   @patch.object(BootstrapDefault, "getRemoteName")
   def test_getOsCheckScriptRemoteLocation(self, getRemoteName_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -328,7 +328,7 @@ class TestBootstrap(TestCase):
 
   @patch.object(BootstrapDefault, "is_suse")
   def test_getRepoFile(self, is_suse_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -342,7 +342,7 @@ class TestBootstrap(TestCase):
   @patch.object(HostLog, "write")
   def test_createTargetDir(self, write_mock, run_mock,
                             init_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -351,7 +351,7 @@ class TestBootstrap(TestCase):
     run_mock.return_value = expected
     res = bootstrap_obj.createTargetDir()
     self.assertEquals(res, expected)
-    command = str(init_mock.call_args[0][3])
+    command = str(init_mock.call_args[0][4])
     self.assertEqual(command,
                      "sudo mkdir -p /var/lib/ambari-agent/tmp ; "
                      "sudo chown -R root /var/lib/ambari-agent/tmp ; "
@@ -366,7 +366,7 @@ class TestBootstrap(TestCase):
   @patch.object(HostLog, "write")
   def test_copyOsCheckScript(self, write_mock, run_mock, init_mock,
                     getOsCheckScriptRemoteLocation_mock, getOsCheckScript_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -377,8 +377,8 @@ class TestBootstrap(TestCase):
     run_mock.return_value = expected
     res = bootstrap_obj.copyOsCheckScript()
     self.assertEquals(res, expected)
-    input_file = str(init_mock.call_args[0][3])
-    remote_file = str(init_mock.call_args[0][4])
+    input_file = str(init_mock.call_args[0][4])
+    remote_file = str(init_mock.call_args[0][5])
     self.assertEqual(input_file, "OsCheckScript")
     self.assertEqual(remote_file, "OsCheckScriptRemoteLocation")
 
@@ -389,7 +389,7 @@ class TestBootstrap(TestCase):
   @patch.object(OSCheck, "is_ubuntu_family")
   @patch.object(OSCheck, "is_redhat_family")
   def test_getRepoFile(self, is_redhat_family, is_ubuntu_family, is_suse_family, hasPassword_mock, getRemoteName_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     is_redhat_family.return_value = True
@@ -437,7 +437,7 @@ class TestBootstrap(TestCase):
     os_path_exists_mock.side_effect = os_path_exists_side_effect
     os_path_exists_mock.return_value = None
 
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     is_redhat_family.return_value = True
@@ -459,11 +459,11 @@ class TestBootstrap(TestCase):
     ssh_run_mock.side_effect = [expected2, expected4]
     res = bootstrap_obj.copyNeededFiles()
     self.assertEquals(res, expected1["exitstatus"])
-    input_file = str(scp_init_mock.call_args[0][3])
-    remote_file = str(scp_init_mock.call_args[0][4])
+    input_file = str(scp_init_mock.call_args[0][4])
+    remote_file = str(scp_init_mock.call_args[0][5])
     self.assertEqual(input_file, "setupAgentFile")
     self.assertEqual(remote_file, "RemoteName")
-    command = str(ssh_init_mock.call_args[0][3])
+    command = str(ssh_init_mock.call_args[0][4])
     self.assertEqual(command, "sudo chmod 644 RepoFile")
     # Another order
     expected1 = {"exitstatus": 0, "log": "log0", "errormsg": "errorMsg"}
@@ -507,7 +507,7 @@ class TestBootstrap(TestCase):
   @patch.object(HostLog, "write")
   def test_runOsCheckScript(self, write_mock, run_mock,
                             init_mock, getOsCheckScriptRemoteLocation_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -517,7 +517,7 @@ class TestBootstrap(TestCase):
     run_mock.return_value = expected
     res = bootstrap_obj.runOsCheckScript()
     self.assertEquals(res, expected)
-    command = str(init_mock.call_args[0][3])
+    command = str(init_mock.call_args[0][4])
     self.assertEqual(command,
                      "chmod a+x OsCheckScriptRemoteLocation && "
                      "env PYTHONPATH=$PYTHONPATH:/var/lib/ambari-agent/tmp OsCheckScriptRemoteLocation centos6")
@@ -529,7 +529,7 @@ class TestBootstrap(TestCase):
   @patch.object(HostLog, "write")
   def test_runSetupAgent(self, write_mock, run_mock,
                          getRunSetupCommand_mock, init_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -539,7 +539,7 @@ class TestBootstrap(TestCase):
     run_mock.return_value = expected
     res = bootstrap_obj.runSetupAgent()
     self.assertEquals(res, expected)
-    command = str(init_mock.call_args[0][3])
+    command = str(init_mock.call_args[0][4])
     self.assertEqual(command, "RunSetupCommand")
 
 
@@ -549,7 +549,7 @@ class TestBootstrap(TestCase):
   def test_getRunSetupCommand(self, getRunSetupWithoutPasswordCommand_mock,
                               getRunSetupWithPasswordCommand_mock,
                               hasPassword_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -568,7 +568,7 @@ class TestBootstrap(TestCase):
   @patch.object(HostLog, "write")
   def test_createDoneFile(self, write_mock):
     tmp_dir = tempfile.gettempdir()
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", tmp_dir,
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", tmp_dir,
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -587,7 +587,7 @@ class TestBootstrap(TestCase):
   @patch.object(SSH, "run")
   @patch.object(HostLog, "write")
   def test_checkSudoPackage(self, write_mock, run_mock, init_mock, is_redhat_family, is_ubuntu_family, is_suse_family):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -599,7 +599,7 @@ class TestBootstrap(TestCase):
     is_suse_family.return_value = False
     res = bootstrap_obj.checkSudoPackage()
     self.assertEquals(res, expected)
-    command = str(init_mock.call_args[0][3])
+    command = str(init_mock.call_args[0][4])
     self.assertEqual(command, "rpm -qa | grep -e '^sudo\-'")
 
   @patch.object(OSCheck, "is_suse_family")
@@ -610,7 +610,7 @@ class TestBootstrap(TestCase):
   @patch.object(HostLog, "write")
   def test_checkSudoPackageUbuntu(self, write_mock, run_mock, init_mock,
                                   is_redhat_family, is_ubuntu_family, is_suse_family):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "ubuntu12",
                                None, "8440", "root")
     is_redhat_family.return_value = False
@@ -622,7 +622,7 @@ class TestBootstrap(TestCase):
     run_mock.return_value = expected
     res = bootstrap_obj.checkSudoPackage()
     self.assertEquals(res, expected)
-    command = str(init_mock.call_args[0][3])
+    command = str(init_mock.call_args[0][4])
     self.assertEqual(command, "dpkg --get-selections|grep -e '^sudo\s*install'")
 
 
@@ -632,7 +632,7 @@ class TestBootstrap(TestCase):
   @patch.object(BootstrapDefault, "getPasswordFile")
   def test_deletePasswordFile(self, getPasswordFile_mock, write_mock, run_mock,
                               init_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -642,7 +642,7 @@ class TestBootstrap(TestCase):
     run_mock.return_value = expected
     res = bootstrap_obj.deletePasswordFile()
     self.assertEquals(res, expected)
-    command = str(init_mock.call_args[0][3])
+    command = str(init_mock.call_args[0][4])
     self.assertEqual(command, "rm PasswordFile")
 
 
@@ -655,7 +655,7 @@ class TestBootstrap(TestCase):
   def test_copyPasswordFile(self, write_mock, ssh_run_mock,
                             ssh_init_mock, scp_run_mock,
                             scp_init_mock, getPasswordFile_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root", password_file="PasswordFile")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -669,11 +669,11 @@ class TestBootstrap(TestCase):
     ssh_run_mock.return_value = expected2
     res = bootstrap_obj.copyPasswordFile()
     self.assertEquals(res, expected1["exitstatus"])
-    input_file = str(scp_init_mock.call_args[0][3])
+    input_file = str(scp_init_mock.call_args[0][4])
     remote_file = str(scp_init_mock.call_args[0][4])
     self.assertEqual(input_file, "PasswordFile")
     self.assertEqual(remote_file, "PasswordFile")
-    command = str(ssh_init_mock.call_args[0][3])
+    command = str(ssh_init_mock.call_args[0][4])
     self.assertEqual(command, "chmod 600 PasswordFile")
     # Another order
     expected1 = {"exitstatus": 0, "log": "log0", "errormsg": "errorMsg"}
@@ -688,7 +688,7 @@ class TestBootstrap(TestCase):
   @patch.object(BootstrapDefault, "getPasswordFile")
   def test_changePasswordFileModeOnHost(self, getPasswordFile_mock, write_mock,
                                         run_mock, init_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -698,14 +698,14 @@ class TestBootstrap(TestCase):
     run_mock.return_value = expected
     res = bootstrap_obj.changePasswordFileModeOnHost()
     self.assertEquals(res, expected)
-    command = str(init_mock.call_args[0][3])
+    command = str(init_mock.call_args[0][4])
     self.assertEqual(command, "chmod 600 PasswordFile")
 
 
   @patch.object(HostLog, "write")
   def test_try_to_execute(self, write_mock):
     expected = 43
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -738,7 +738,7 @@ class TestBootstrap(TestCase):
   @patch("logging.error")
   def test_run(self, error_mock, warn_mock, write_mock, createDoneFile_mock,
                hasPassword_mock, try_to_execute_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -810,7 +810,7 @@ class TestBootstrap(TestCase):
   @patch.object(BootstrapDefault, "createDoneFile")
   @patch.object(HostLog, "write")
   def test_interruptBootstrap(self, write_mock, createDoneFile_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     bootstrap_obj = Bootstrap("hostname", shared_state)
@@ -827,7 +827,7 @@ class TestBootstrap(TestCase):
   @patch.object(BootstrapDefault, "getStatus")
   def test_PBootstrap(self, getStatus_mock, interruptBootstrap_mock, start_mock,
                       info_mock, warn_mock, time_mock, sleep_mock):
-    shared_state = SharedState("root", "sshkey_file", "scriptDir", "bootdir",
+    shared_state = SharedState("root", "123", "sshkey_file", "scriptDir", "bootdir",
                                "setupAgentFile", "ambariServer", "centos6",
                                None, "8440", "root")
     n = 180

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-web/app/controllers/wizard.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard.js b/ambari-web/app/controllers/wizard.js
index fdee580..517122f 100644
--- a/ambari-web/app/controllers/wizard.js
+++ b/ambari-web/app/controllers/wizard.js
@@ -616,6 +616,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     sshKey: "", //string
     bootRequestId: null, //string
     sshUser: "root", //string
+    sshPort: "22",
     agentUser: "root" //string
   },
 
@@ -628,6 +629,7 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
     sshKey: "", //string
     bootRequestId: null, //string
     sshUser: "", //string
+    sshPort: "22",
     agentUser: "" //string
   },
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-web/app/controllers/wizard/step2_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step2_controller.js b/ambari-web/app/controllers/wizard/step2_controller.js
index 3b51761..d951102 100644
--- a/ambari-web/app/controllers/wizard/step2_controller.js
+++ b/ambari-web/app/controllers/wizard/step2_controller.js
@@ -94,6 +94,14 @@ App.WizardStep2Controller = Em.Controller.extend({
   }.property('content.installOptions.sshUser'),
 
   /**
+   * "Shortcut" to <code>content.installOptions.sshPort</code>
+   * @type {string}
+   */
+  sshPort: function () {
+    return this.get('content.installOptions.sshPort');
+  }.property('content.installOptions.sshPort'),
+
+  /**
    * "Shortcut" to <code>content.installOptions.agentUser</code>
    * @type {string}
    */
@@ -148,6 +156,17 @@ App.WizardStep2Controller = Em.Controller.extend({
   }.property('sshUser', 'useSSH', 'hasSubmitted', 'manualInstall'),
 
   /**
+   * Error-message if <code>sshPort</code> is empty, null otherwise
+   * @type {string|null}
+   */
+  sshPortError: function () {
+    if (this.get('manualInstall') === false && this.get('useSSH') && Em.isEmpty(this.get('sshPort').trim() ))  {
+      return Em.I18n.t('installer.step2.sshPort.required');
+    }
+    return null;
+  }.property('sshPort', 'useSSH', 'hasSubmitted', 'manualInstall'),
+
+  /**
    * Error-message if <code>agentUser</code> is empty, null otherwise
    * @type {string|null}
    */
@@ -163,8 +182,8 @@ App.WizardStep2Controller = Em.Controller.extend({
    * @type {bool}
    */
   isSubmitDisabled: function () {
-    return (this.get('hostsError') || this.get('sshKeyError') || this.get('sshUserError') || this.get('agentUserError'));
-  }.property('hostsError', 'sshKeyError', 'sshUserError', 'agentUserError'),
+    return (this.get('hostsError') || this.get('sshKeyError') || this.get('sshUserError') || this.get('sshPortError') || this.get('agentUserError'));
+  }.property('hostsError', 'sshKeyError', 'sshUserError', 'sshPortError', 'agentUserError'),
 
   installedHostNames: function () {
     var installedHostsName = [];
@@ -292,7 +311,7 @@ App.WizardStep2Controller = Em.Controller.extend({
       this.set('hostsError', Em.I18n.t('installer.step2.hostName.error.already_installed'));
     }
 
-    if (this.get('hostsError') || this.get('sshUserError') || this.get('agentUserError') || this.get('sshKeyError')) {
+    if (this.get('hostsError') || this.get('sshUserError') || this.get('sshPortError') || this.get('agentUserError') || this.get('sshKeyError')) {
       return false;
     }
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-web/app/controllers/wizard/step3_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/wizard/step3_controller.js b/ambari-web/app/controllers/wizard/step3_controller.js
index b3645e9..efea446 100644
--- a/ambari-web/app/controllers/wizard/step3_controller.js
+++ b/ambari-web/app/controllers/wizard/step3_controller.js
@@ -278,6 +278,7 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, {
         'sshKey': this.get('content.installOptions.sshKey'),
         'hosts': this.getBootstrapHosts(),
         'user': this.get('content.installOptions.sshUser'),
+        'sshPort': this.get('content.installOptions.sshPort'),
         'userRunAs': App.get('supports.customizeAgentUserAccount') ? this.get('content.installOptions.agentUser') : 'root'
     });
     App.router.get(this.get('content.controllerName')).launchBootstrap(bootStrapData, function (requestId) {
@@ -461,6 +462,7 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, {
         'sshKey': this.get('content.installOptions.sshKey'),
         'hosts': hosts.mapProperty('name'),
         'user': this.get('content.installOptions.sshUser'),
+        'sshPort': this.get('content.installOptions.sshPort'),
         'userRunAs': App.get('supports.customizeAgentUserAccount') ? this.get('content.installOptions.agentUser') : 'root'
       });
     this.set('numPolls', 0);

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-web/app/messages.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js
index 8ee266c..c12ba24 100644
--- a/ambari-web/app/messages.js
+++ b/ambari-web/app/messages.js
@@ -586,6 +586,9 @@ Em.I18n.translations = {
   'installer.step2.sshUser.toolTip':'The user account used to install the Ambari Agent on the target host(s) via SSH. This user must be set up with passwordless SSH and sudo access on all the target host(s)',
   'installer.step2.sshUser.placeholder':'Enter user name',
   'installer.step2.sshUser.required':'User name is required',
+  'installer.step2.sshPort':'SSH Port Number',
+  'installer.step2.sshPort.toolTip':'SSH Port Number',
+  'installer.step2.sshPort.required':'SSH Port Number is required.',
   'installer.step2.agentUser':'Ambari Agent User Account',
   'installer.step2.agentUser.toolTip':'The user account used to run the Ambari Agent daemon on the target host(s). This user must be set up with passwordless sudo access on all the target host(s)',
   'installer.step2.bootStrap.error':'Errors were encountered while setting up Ambari Agents on the hosts.',

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index dae745ae..8486411 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -719,6 +719,10 @@ h1 {
       margin-right: 10px;
       padding-top: 5px;
     }
+    .ssh-port {
+      margin-right: 10px;
+      padding-top: 5px;
+    }
     #targetHosts {
       .target-hosts-input {
         padding-left: 18px;

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-web/app/templates/wizard/step2.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/wizard/step2.hbs b/ambari-web/app/templates/wizard/step2.hbs
index a194cc0..5aa4f89 100644
--- a/ambari-web/app/templates/wizard/step2.hbs
+++ b/ambari-web/app/templates/wizard/step2.hbs
@@ -90,6 +90,18 @@
               {{/if}}
             </div>
           </div>
+           <div class="row-fluid">
+                <label rel="tooltip" {{translateAttr title="installer.step2.sshPort.toolTip"}} class="ssh-port pull-left span4">
+                    {{t installer.step2.sshPort}}
+                </label>
+
+                <div {{bindAttr class="sshPortError:error :control-group"}}>
+                    {{view view.textFieldView valueBinding="content.installOptions.sshPort" isEnabledBinding="content.installOptions.useSsh" }}
+                    {{#if sshPortError}}
+                        <span class="help-inline">{{sshPortError}}</span>
+                    {{/if}}
+                </div>
+           </div>
           {{#if App.supports.customizeAgentUserAccount}}
             <div class="row-fluid">
               <label rel="tooltip" {{translateAttr title="installer.step2.agentUser.toolTip"}} class="ssh-user pull-left span4">

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-web/test/controllers/wizard/step2_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step2_test.js b/ambari-web/test/controllers/wizard/step2_test.js
index d62b247..8f0fc66 100644
--- a/ambari-web/test/controllers/wizard/step2_test.js
+++ b/ambari-web/test/controllers/wizard/step2_test.js
@@ -29,21 +29,25 @@ describe('App.WizardStep2Controller', function () {
     {
       manualInstall: false,
       user: '',
+      sshPort:'',
       e: ''
     },
     {
       manualInstall: true,
       user: '',
+      sshPort:'',
       e: null
     },
     {
       manualInstall: true,
       user: 'nobody',
+      sshPort:'123',
       e: null
     },
     {
       manualInstall: false,
       user: 'nobody',
+      sshPort:'123',
       e: null
     }
   ]);
@@ -100,6 +104,15 @@ describe('App.WizardStep2Controller', function () {
     });
   });
 
+  describe('#sshPort', function() {
+      it('should be equal to content.installOptions.sshPort', function() {
+          var controller = App.WizardStep2Controller.create({content: {installOptions: {sshPort: '123'}}});
+          expect(controller.get('sshPort')).to.equal('123');
+          controller.set('content.installOptions.sshPort', '321');
+          expect(controller.get('sshPort')).to.equal('321');
+      });
+  });
+
   describe('#agentUser', function() {
     it('should be equal to content.installOptions.agentUser', function() {
       var controller = App.WizardStep2Controller.create({content: {installOptions: {agentUser: '123'}}});
@@ -285,6 +298,22 @@ describe('App.WizardStep2Controller', function () {
 
   });
 
+  describe('#sshPortError', function () {
+
+      userErrorTests.forEach(function(test) {
+          it('', function() {
+              var controller = App.WizardStep2Controller.create({content: {installOptions: {manualInstall: test.manualInstall, sshPort: test.sshPort}}});
+              if(Em.isNone(test.e)) {
+                  expect(controller.get('sshPortError')).to.equal(null);
+              }
+              else {
+                  expect(controller.get('sshPortError').length).to.be.above(2);
+              }
+          });
+      });
+
+  });
+
   describe('#agentUserError', function () {
 
     afterEach(function () {
@@ -379,6 +408,15 @@ describe('App.WizardStep2Controller', function () {
       expect(controller.evaluateStep()).to.equal(false);
     });
 
+    it('should return false if sshPortError is not empty', function () {
+        var controller = App.WizardStep2Controller.create({
+            hostNames: 'apache.ambari',
+            parseHostNamesAsPatternExpression: Em.K
+        });
+        controller.reopen({sshPortError: 'error'});
+        expect(controller.evaluateStep()).to.equal(false);
+    });
+
     it('should return false if agentUserError is not empty', function () {
       var controller = App.WizardStep2Controller.create({
         hostNames: 'apache.ambari',
@@ -487,6 +525,7 @@ describe('App.WizardStep2Controller', function () {
       hostsError: '',
       sshKeyError: '',
       sshUserError: '',
+      sshPortError: '',
       agentUserError: ''
     });
 
@@ -512,6 +551,12 @@ describe('App.WizardStep2Controller', function () {
       controller.set('sshUserError', '');
       expect(controller.get('isSubmitDisabled').length).to.above(0);
     });
+
+    it('should return value if sshPortError is not empty', function () {
+        controller.set('sshPortError', 'error');
+        controller.set('agentUserError', '');
+        expect(controller.get('isSubmitDisabled').length).to.above(0);
+    });
   });
 
   describe('#installedHostsPopup', function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/39c04ac9/ambari-web/test/controllers/wizard/step3_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/wizard/step3_test.js b/ambari-web/test/controllers/wizard/step3_test.js
index 74c62e6..f48514c 100644
--- a/ambari-web/test/controllers/wizard/step3_test.js
+++ b/ambari-web/test/controllers/wizard/step3_test.js
@@ -558,6 +558,7 @@ describe('App.WizardStep3Controller', function () {
             installOptions: {
               sshKey: 'key',
               sshUser: 'root',
+              sshPort: '123',
               agentUser: 'user'
             },
             hosts: { "host0": { "name": "host0" }, "host1": { "name": "host1" } }
@@ -571,6 +572,7 @@ describe('App.WizardStep3Controller', function () {
           sshKey: 'key',
           hosts: ['host0', 'host1'],
           user: 'root',
+          sshPort: '123',
           userRunAs: item.userRunAs
         }));
       });
@@ -2410,6 +2412,7 @@ describe('App.WizardStep3Controller', function () {
           installOptions: {
             sshKey: 'key',
             sshUser: 'root',
+            sshPort: '123',
             agentUser: 'user'
           },
           hosts: { "host0": { "name": "host0" }, "host1": { "name": "host1" } },
@@ -2436,6 +2439,7 @@ describe('App.WizardStep3Controller', function () {
           sshKey: 'key',
           hosts: ['host0', 'host1'],
           user: 'root',
+          sshPort: '123',
           userRunAs: item.userRunAs
         }));
       });