From b1d9919740fe1aafb62e9f58585ac2bbf8d49b19 Mon Sep 17 00:00:00 2001
From: Johannes Renner <jrenner@suse.com>
Date: Wed, 29 Jun 2016 17:25:51 +0200
Subject: [PATCH 32/38] Support remote port forwarding with salt-ssh (cherry
 picked from commit e421e4d)

---
 salt/client/ssh/__init__.py |  7 ++++++-
 salt/client/ssh/shell.py    | 13 +++++++++++--
 salt/utils/parsers.py       | 14 ++++++++++++++
 3 files changed, 31 insertions(+), 3 deletions(-)

diff --git a/salt/client/ssh/__init__.py b/salt/client/ssh/__init__.py
index a5e8199..9b942ec 100644
--- a/salt/client/ssh/__init__.py
+++ b/salt/client/ssh/__init__.py
@@ -243,6 +243,9 @@ class SSH(object):
                 'ssh_identities_only',
                 salt.config.DEFAULT_MASTER_OPTS['ssh_identities_only']
             ),
+            'remote_port_forwards': self.opts.get(
+                'ssh_remote_port_forwards'
+            ),
         }
         if self.opts.get('rand_thin_dir'):
             self.defaults['thin_dir'] = os.path.join(
@@ -621,6 +624,7 @@ class Single(object):
             minion_opts=None,
             identities_only=False,
             sudo_user=None,
+            remote_port_forwards=None,
             **kwargs):
         # Get mine setting and mine_functions if defined in kwargs (from roster)
         self.mine = mine
@@ -669,7 +673,8 @@ class Single(object):
                 'tty': tty,
                 'mods': self.mods,
                 'identities_only': identities_only,
-                'sudo_user': sudo_user}
+                'sudo_user': sudo_user,
+                'remote_port_forwards': remote_port_forwards}
         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 f08a877..bc44d79 100644
--- a/salt/client/ssh/shell.py
+++ b/salt/client/ssh/shell.py
@@ -59,7 +59,8 @@ class Shell(object):
             tty=False,
             mods=None,
             identities_only=False,
-            sudo_user=None):
+            sudo_user=None,
+            remote_port_forwards=None):
         self.opts = opts
         self.host = host
         self.user = user
@@ -71,6 +72,7 @@ class Shell(object):
         self.tty = tty
         self.mods = mods
         self.identities_only = identities_only
+        self.remote_port_forwards = remote_port_forwards
 
     def get_error(self, errstr):
         '''
@@ -214,11 +216,18 @@ class Shell(object):
             opts = self._passwd_opts()
         if self.priv:
             opts = self._key_opts()
-        return "{0} {1} {2} {3} {4}".format(
+
+        ports = ''
+        if self.remote_port_forwards:
+            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(
                 ssh,
                 '' if ssh == 'scp' else self.host,
                 '-t -t' if tty else '',
                 opts,
+                '' if ssh == 'scp' else ports,
                 cmd)
 
     def _old_run_cmd(self, cmd):
diff --git a/salt/utils/parsers.py b/salt/utils/parsers.py
index 5a741fb..5997a01 100644
--- a/salt/utils/parsers.py
+++ b/salt/utils/parsers.py
@@ -2508,6 +2508,20 @@ 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.'
+        )
+        ports_group.add_option(
+            '--remote-port-forwards',
+            dest='ssh_remote_port_forwards',
+            help='Setup remote port forwarding using the same syntax as with '
+                 'the -R parameter of ssh. A comma separated list of port '
+                 'forwarding definitions will be translated into multiple '
+                 '-R parameters.'
+        )
+        self.add_option_group(ports_group)
+
         auth_group = optparse.OptionGroup(
             self, 'Authentication Options',
             'Parameters affecting authentication'
-- 
2.10.2

