From f91062dfea7535a01f304d88f6f9abc143b0c0ac Mon Sep 17 00:00:00 2001
From: Matei Albu <malbu@suse.de>
Date: Mon, 19 Dec 2016 16:54:52 +0100
Subject: [PATCH] Add --ssh-option to salt-ssh

--ssh-option can be used to pass -o options to the ssh client.
(cherry picked from commit 16f21e5)

Check if ssh_options present
---
 salt/client/ssh/__init__.py |  7 ++++++-
 salt/client/ssh/shell.py    | 19 +++++++++++++++----
 salt/utils/parsers.py       | 18 +++++++++++++-----
 3 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py
index 9b942ec..b2067a0 100644
--- a/salt/client/ssh/__init__.py
+++ b/salt/client/ssh/__init__.py
@@ -246,6 +246,9 @@ class SSH(object):
             'remote_port_forwards': self.opts.get(
                 'ssh_remote_port_forwards'
             ),
+            'ssh_options': self.opts.get(
+                'ssh_options'
+            )
         }
         if self.opts.get('rand_thin_dir'):
             self.defaults['thin_dir'] = os.path.join(
@@ -625,6 +628,7 @@ class Single(object):
             identities_only=False,
             sudo_user=None,
             remote_port_forwards=None,
+            ssh_options=None,
             **kwargs):
         # Get mine setting and mine_functions if defined in kwargs (from roster)
         self.mine = mine
@@ -674,7 +678,8 @@ class Single(object):
                 'mods': self.mods,
                 'identities_only': identities_only,
                 'sudo_user': sudo_user,
-                'remote_port_forwards': remote_port_forwards}
+                'remote_port_forwards': remote_port_forwards,
+                'ssh_options': ssh_options}
         self.minion_opts = opts.get('ssh_minion_opts', {})
         if minion_opts is not None:
             self.minion_opts.update(minion_opts)
diff --git a/salt/client/ssh/shell.py b/salt/client/ssh/shell.py
index bc44d79..deb6519 100644
--- a/salt/client/ssh/shell.py
+++ b/salt/client/ssh/shell.py
@@ -60,7 +60,8 @@ class Shell(object):
             mods=None,
             identities_only=False,
             sudo_user=None,
-            remote_port_forwards=None):
+            remote_port_forwards=None,
+            ssh_options=None):
         self.opts = opts
         self.host = host
         self.user = user
@@ -73,6 +74,7 @@ class Shell(object):
         self.mods = mods
         self.identities_only = identities_only
         self.remote_port_forwards = remote_port_forwards
+        self.ssh_options = ssh_options
 
     def get_error(self, errstr):
         '''
@@ -158,6 +160,12 @@ class Shell(object):
             ret.append('-o {0} '.format(option))
         return ''.join(ret)
 
+    def _ssh_opts(self):
+        if self.ssh_options:
+            return ' '.join(['-o {0}'.format(opt)
+                            for opt in self.ssh_options])
+        return ''
+
     def _copy_id_str_old(self):
         '''
         Return the string to execute ssh-copy-id
@@ -165,11 +173,12 @@ class Shell(object):
         if self.passwd:
             # Using single quotes prevents shell expansion and
             # passwords containing '$'
-            return "{0} {1} '{2} -p {3} {4}@{5}'".format(
+            return "{0} {1} '{2} -p {3} {4} {5}@{6}'".format(
                     'ssh-copy-id',
                     '-i {0}.pub'.format(self.priv),
                     self._passwd_opts(),
                     self.port,
+                    self._ssh_opts(),
                     self.user,
                     self.host)
         return None
@@ -182,11 +191,12 @@ class Shell(object):
         if self.passwd:
             # Using single quotes prevents shell expansion and
             # passwords containing '$'
-            return "{0} {1} {2} -p {3} {4}@{5}".format(
+            return "{0} {1} {2} -p {3} {4} {5}@{6}".format(
                     'ssh-copy-id',
                     '-i {0}.pub'.format(self.priv),
                     self._passwd_opts(),
                     self.port,
+                    self._ssh_opts(),
                     self.user,
                     self.host)
         return None
@@ -222,12 +232,13 @@ class Shell(object):
             port_forwards = self.remote_port_forwards.split(',')
             ports = ' '.join(map(lambda x: '-R {0}'.format(x), port_forwards))
 
-        return "{0} {1} {2} {3} {4} {5}".format(
+        return "{0} {1} {2} {3} {4} {5} {6}".format(
                 ssh,
                 '' if ssh == 'scp' else self.host,
                 '-t -t' if tty else '',
                 opts,
                 '' if ssh == 'scp' else ports,
+                self._ssh_opts() if self.ssh_options else '',
                 cmd)
 
     def _old_run_cmd(self, cmd):
diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py
index 5997a01..94f218e 100644
--- a/salt/utils/parsers.py
+++ b/salt/utils/parsers.py
@@ -2508,11 +2508,11 @@ class SaltSSHOptionParser(six.with_metaclass(OptionParserMeta,
             help=('Select a random temp dir to deploy on the remote system. '
                   'The dir will be cleaned after the execution.'))
 
-        ports_group = optparse.OptionGroup(
-            self, 'Port Forwarding Options',
-            'Parameters for setting up SSH port forwarding.'
+        ssh_group = optparse.OptionGroup(
+            self, 'SSH Options',
+            'Parameters for the SSH client.'
         )
-        ports_group.add_option(
+        ssh_group.add_option(
             '--remote-port-forwards',
             dest='ssh_remote_port_forwards',
             help='Setup remote port forwarding using the same syntax as with '
@@ -2520,7 +2520,15 @@ class SaltSSHOptionParser(six.with_metaclass(OptionParserMeta,
                  'forwarding definitions will be translated into multiple '
                  '-R parameters.'
         )
-        self.add_option_group(ports_group)
+        ssh_group.add_option(
+            '--ssh-option',
+            dest='ssh_options',
+            action='append',
+            help='Equivalent to the -o ssh command option. Passes options to '
+                 'the SSH client in the format used in the client configuration file. '
+                 'Can be used multiple times.'
+        )
+        self.add_option_group(ssh_group)
 
         auth_group = optparse.OptionGroup(
             self, 'Authentication Options',
-- 
2.6.6

