1- from typing import Dict , Union
1+ from typing import Dict , Optional , Union
22import os
3- import asyncio
43from pathlib import Path
5- from bale .result import Result
6- from bale .interfaces .cli import Cli
4+ from bale .interfaces import cli
75
86
9- def get_hosts (path ):
7+ def get_hosts (path : str = "data" ):
108 path = f"{ Path (path ).resolve ()} /config"
119 hosts = []
1210 try :
@@ -20,32 +18,42 @@ def get_hosts(path):
2018 return []
2119
2220
23- async def get_public_key (path : str ) -> str :
21+ async def get_public_key (path : str = "data" ) -> str :
2422 path = Path (path ).resolve ()
2523 if "id_rsa.pub" not in os .listdir (path ) or "id_rsa" not in os .listdir (path ):
26- await Cli ().shell (f"""ssh-keygen -t rsa -N "" -f { path } /id_rsa""" )
24+ await cli . Cli ().shell (f"""ssh-keygen -t rsa -N "" -f { path } /id_rsa""" )
2725 with open (f"{ path } /id_rsa.pub" , "r" , encoding = "utf-8" ) as reader :
2826 return reader .read ()
2927
3028
31- class Ssh (Cli ):
32- def __init__ (self , path : str , host : str , hostname : str = "" , username : str = "" , password : Union [str , None ] = None , seperator : bytes = b"\n " ) -> None :
29+ class Ssh (cli .Cli ):
30+ def __init__ (
31+ self ,
32+ host : str ,
33+ hostname : str = "" ,
34+ username : str = "" ,
35+ password : Optional [str ] = None ,
36+ options : Optional [Dict [str , str ]] = None ,
37+ path : str = "data" ,
38+ seperator : bytes = b"\n " ,
39+ ) -> None :
3340 super ().__init__ (seperator = seperator )
3441 self ._raw_path : str = path
3542 self ._path : Path = Path (path ).resolve ()
36- self .host : str = host
43+ self .host : str = host . replace ( " " , "" )
3744 self .password : Union [str , None ] = password
3845 self .use_key : bool = False
3946 if password is None :
4047 self .use_key = True
48+ self .options : Optional [Dict [str , str ]] = options
4149 self .key_path : str = f"{ self ._path } /id_rsa"
42- self ._base_cmd : str = ""
43- self ._full_cmd : str = ""
50+ self ._base_command : str = ""
51+ self ._full_command : str = ""
4452 self ._config_path : str = f"{ self ._path } /config"
4553 self ._config : Dict [str , Dict [str , str ]] = {}
4654 self .read_config ()
47- self .hostname : str = hostname or self ._config .get (host , {}).get ("HostName" , "" )
48- self .username : str = username or self ._config .get (host , {}).get ("User" , "" )
55+ self .hostname : str = hostname or self ._config .get (host . replace ( " " , "" ) , {}).get ("HostName" , "" )
56+ self .username : str = username or self ._config .get (host . replace ( " " , "" ) , {}).get ("User" , "" )
4957 self .set_config ()
5058
5159 def read_config (self ) -> None :
@@ -57,7 +65,7 @@ def read_config(self) -> None:
5765 if line == "" or line .startswith ("#" ):
5866 continue
5967 if line .startswith ("Host " ):
60- current_host = line .split (" " )[1 ].strip ()
68+ current_host = line .split (" " , 1 )[1 ].strip (). replace ( '"' , "" )
6169 self ._config [current_host ] = {}
6270 else :
6371 key , value = line .split (" " , 1 )
@@ -76,30 +84,40 @@ def write_config(self) -> None:
7684 def set_config (self ) -> None :
7785 self ._config [self .host ] = {
7886 "IdentityFile" : self .key_path ,
79- "PasswordAuthentication" : "no" ,
8087 "StrictHostKeychecking" : "no" ,
8188 "IdentitiesOnly" : "yes" ,
8289 }
90+ self ._config [self .host ]["PasswordAuthentication" ] = "no" if self .password is None else "yes"
8391 if self .hostname != "" :
8492 self ._config [self .host ]["HostName" ] = self .hostname
8593 if self .username != "" :
8694 self ._config [self .host ]["User" ] = self .username
95+ if self .options is not None :
96+ self ._config [self .host ].update (self .options )
8797 self .write_config ()
8898
8999 def remove (self ) -> None :
90100 del self ._config [self .host ]
91101 self .write_config ()
92102
93- async def execute (self , command : str , max_output_lines : int = 0 ) -> Result :
94- self ._base_cmd = f"{ '' if self .use_key else f'sshpass -p { self .password } ' } ssh -F { self ._config_path } { self .host } "
95- self ._full_cmd = f"{ self ._base_cmd } { command } "
96- return await super ().execute (self ._full_cmd , max_output_lines )
103+ async def execute (self , command : str , max_output_lines : int = 0 ) -> cli .Result :
104+ self ._full_command = f"{ self .base_command } { command } "
105+ return await super ().execute (self ._full_command , max_output_lines )
97106
98- async def send_key (self ) -> Result :
107+ async def shell (self , command : str , max_output_lines : int = 0 ) -> cli .Result :
108+ self ._full_command = f"{ self .base_command } { command } "
109+ return await super ().shell (self ._full_command , max_output_lines )
110+
111+ async def send_key (self ) -> cli .Result :
99112 await get_public_key (self ._raw_path )
100113 cmd = f"sshpass -p { self .password } " f"ssh-copy-id -o IdentitiesOnly=yes -i { self .key_path } " f"-o StrictHostKeychecking=no { self .username } @{ self .hostname } "
101- return await super ().execute (cmd )
114+ return await super ().shell (cmd )
102115
103116 @property
104117 def config_path (self ):
105118 return self ._config_path
119+
120+ @property
121+ def base_command (self ):
122+ self ._base_command = f'{ "" if self .use_key else f"sshpass -p { self .password } " } ssh -F { self ._config_path } { self .host } '
123+ return self ._base_command
0 commit comments