@@ -653,6 +653,30 @@ def sshPrivateKeyFolder(self):
653
653
def setSshPrivateKeyFile (self , value , profile_id = None ):
654
654
self .setProfileStrValue ('snapshots.ssh.private_key_file' , value , profile_id )
655
655
656
+ def sshProxyHost (self , profile_id = None ):
657
+ #?Proxy host used to connect to remote host.;;IP or domain address
658
+ return self .profileStrValue ('snapshots.ssh.proxy_host' , '' , profile_id )
659
+
660
+ def setSshProxyHost (self , value , profile_id = None ):
661
+ self .setProfileStrValue ('snapshots.ssh.proxy_host' , value , profile_id )
662
+
663
+ def sshProxyPort (self , profile_id = None ):
664
+ #?Proxy host port used to connect to remote host.;0-65535
665
+ return self .profileIntValue (
666
+ 'snapshots.ssh.proxy_host_port' , '22' , profile_id )
667
+
668
+ def setSshProxyPort (self , value , profile_id = None ):
669
+ self .setProfileIntValue (
670
+ 'snapshots.ssh.proxy_host_port' , value , profile_id )
671
+
672
+ def sshProxyUser (self , profile_id = None ):
673
+ #?Remote SSH user;;local users name
674
+ return self .profileStrValue (
675
+ 'snapshots.ssh.proxy_user' , getpass .getuser (), profile_id )
676
+
677
+ def setSshProxyUser (self , value , profile_id = None ):
678
+ self .setProfileStrValue ('snapshots.ssh.proxy_user' , value , profile_id )
679
+
656
680
def sshMaxArgLength (self , profile_id = None ):
657
681
#?Maximum command length of commands run on remote host. This can be tested
658
682
#?for all ssh profiles in the configuration
@@ -698,16 +722,16 @@ def sshDefaultArgs(self, profile_id = None):
698
722
return args
699
723
700
724
def sshCommand (self ,
701
- cmd = None ,
702
- custom_args = None ,
703
- port = True ,
704
- cipher = True ,
705
- user_host = True ,
706
- ionice = True ,
707
- nice = True ,
708
- quote = False ,
709
- prefix = True ,
710
- profile_id = None ):
725
+ cmd = None ,
726
+ custom_args = None ,
727
+ port = True ,
728
+ cipher = True ,
729
+ user_host = True ,
730
+ ionice = True ,
731
+ nice = True ,
732
+ quote = False ,
733
+ prefix = True ,
734
+ profile_id = None ):
711
735
"""
712
736
Return SSH command with all arguments.
713
737
@@ -726,46 +750,68 @@ def sshCommand(self,
726
750
Returns:
727
751
list: ssh command with chosen arguments
728
752
"""
753
+ # Refactor: Use of assert is discouraged in productive code.
754
+ # Raise Exceptions instead.
729
755
assert cmd is None or isinstance (cmd , list ), "cmd '{}' is not list instance" .format (cmd )
730
756
assert custom_args is None or isinstance (custom_args , list ), "custom_args '{}' is not list instance" .format (custom_args )
731
- ssh = ['ssh' ]
757
+
758
+ ssh = ['ssh' ]
732
759
ssh += self .sshDefaultArgs (profile_id )
760
+
761
+ # Proxy (aka Jump host)
762
+ if self .sshProxyHost (profile_id ):
763
+ ssh += ['-J' , '{}@{}:{}' .format (
764
+ self .sshProxyUser (profile_id ),
765
+ self .sshProxyHost (profile_id ),
766
+ self .sshProxyPort (profile_id )
767
+ )]
768
+
733
769
# remote port
734
770
if port :
735
771
ssh += ['-p' , str (self .sshPort (profile_id ))]
772
+
736
773
# cipher used to transfer data
737
774
c = self .sshCipher (profile_id )
738
775
if cipher and c != 'default' :
739
- ssh += ['-o' , 'Ciphers={}' .format (c )]
776
+ ssh += ['-o' , f'Ciphers={ c } ' ]
777
+
740
778
# custom arguments
741
779
if custom_args :
742
780
ssh += custom_args
781
+
743
782
# user@host
744
783
if user_host :
745
784
ssh .append ('{}@{}' .format (self .sshUser (profile_id ),
746
785
self .sshHost (profile_id )))
747
786
# quote the command running on remote host
748
787
if quote and cmd :
749
788
ssh .append ("'" )
789
+
750
790
# run 'ionice' on remote host
751
791
if ionice and self .ioniceOnRemote (profile_id ) and cmd :
752
792
ssh += ['ionice' , '-c2' , '-n7' ]
793
+
753
794
# run 'nice' on remote host
754
795
if nice and self .niceOnRemote (profile_id ) and cmd :
755
796
ssh += ['nice' , '-n19' ]
797
+
756
798
# run prefix on remote host
757
799
if prefix and cmd and self .sshPrefixEnabled (profile_id ):
758
- ssh += self .sshPrefixCmd (profile_id , cmd_type = list )
800
+ ssh += self .sshPrefixCmd (profile_id , cmd_type = type (cmd ))
801
+
759
802
# add the command
760
803
if cmd :
761
804
ssh += cmd
805
+
762
806
# close quote
763
807
if quote and cmd :
764
808
ssh .append ("'" )
765
809
810
+ logger .debug (f'SSH command: { ssh } ' , self )
811
+
766
812
return ssh
767
813
768
- #ENCFS
814
+ # EncFS
769
815
def localEncfsPath (self , profile_id = None ):
770
816
#?Where to save snapshots in mode 'local_encfs'.;absolute path
771
817
return self .profileStrValue ('snapshots.local_encfs.path' , '' , profile_id )
@@ -1311,17 +1357,25 @@ def setSshPrefix(self, enabled, value, profile_id = None):
1311
1357
self .setProfileBoolValue ('snapshots.ssh.prefix.enabled' , enabled , profile_id )
1312
1358
self .setProfileStrValue ('snapshots.ssh.prefix.value' , value , profile_id )
1313
1359
1314
- def sshPrefixCmd (self , profile_id = None , cmd_type = str ):
1360
+ def sshPrefixCmd (self , profile_id = None , cmd_type = str ):
1361
+ """Return the config value of sshPrefix if enabled.
1362
+
1363
+ Dev note by buhtz (2024-04): Good opportunity to refactor. To much
1364
+ implicit behavior in it.
1365
+ """
1315
1366
if cmd_type == list :
1316
1367
if self .sshPrefixEnabled (profile_id ):
1317
1368
return shlex .split (self .sshPrefix (profile_id ))
1318
- else :
1319
- return []
1369
+
1370
+ return []
1371
+
1320
1372
if cmd_type == str :
1321
1373
if self .sshPrefixEnabled (profile_id ):
1322
1374
return self .sshPrefix (profile_id ).strip () + ' '
1323
- else :
1324
- return ''
1375
+
1376
+ return ''
1377
+
1378
+ raise TypeError (f'Unable to handle type { cmd_type } .' )
1325
1379
1326
1380
def continueOnErrors (self , profile_id = None ):
1327
1381
#?Continue on errors. This will keep incomplete snapshots rather than
0 commit comments