系統工程師神器!如何讓Python在Linux上執行ssh(二)Python Paramiko程式運行@Sunny的工程師日記
近期因為專案的關係,必須同步更新多台Linux設備的程式,但每次要更新3台以上設備就會讓我頭很痛,所以花了一些時間找辦法讓我更新的步驟能夠更快速更簡單。因此我使用了Python Paramiko套件來實現ssh及sftp的工作,實現佈建環境自動化的功能。
系統需求:
5.執行程式。
以上為Paramiko程式碼及如何執行,下一篇會提到如何將寫好的python程式碼編譯成可攜帶的執行檔。
以上如有任何問題,歡迎留言與我討論。
系統需求:
- Linux。
- Python 3.X版本。
- Python pip。
- Paramiko套件。
程式碼:
1.ssh操作。
2.ssh INI文件,將某些常更換的參數以INI文件方式儲存。
3.sftp操作。
4.sftp INI文件,將某些常更換的參數以INI文件方式儲存。
1.ssh操作。
#!/usr/bin/python
# coding:utf-8
import paramiko
import configparser
def readini():
print("hello!")
try:
config = configparser.ConfigParser()
config.read('upload.ini')
address = config.get('REMOTE', 'ipaddress')
port = config.get('REMOTE', 'port')
username = config.get('REMOTE', 'username')
userpwd = config.get('REMOTE', 'password')
suserpwd = config.get('REMOTE', 'sudo_password')
command = config.get('REMOTE', 'command')
except Exception as e:
print('\033[91m<<< Read configuration FAIL! >>>\033[0m', e)
print('IMPORT INI SUCCESS.')
# readini()
port = 22
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(str(address), int(port), str(username), str(userpwd))
stdin, stdout, stderr = ssh.exec_command(command)
print(stdout.readlines())
print("指令完成!")
ssh.close()
# stdin, stdout, stderr = ssh.exec_command("echo moxa|sudo -S whoami")
2.ssh INI文件,將某些常更換的參數以INI文件方式儲存。
[REMOTE]
ipaddress = 192.168.1.90
port = 22
username = 1234
password = 1234
sudo_password = 1234
command = ls
3.sftp操作。
#!/usr/bin/python
# coding=utf-8
import paramiko
import os
import configparser
import datetime
def ssh_chmod(host, port, username, password, remote_dir, dirpathmod, sudo_password):
'''
Change the permission of remote host's upload folder.
@ param host: Remote host's IP.
@ param port: Remote host's port for sftp
@ param username: Remote host's login username for ssh.
@ param password: Remote host's login password for ssh.
@ param remote: Remote host upload folder.
@ param dirpathmod: The permission of remote path.
'''
paramiko.util.log_to_file('ssh.log')
ssh = paramiko.SSHClient()
# Allow connections to hosts that are not in the know_hosts.
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
ssh.connect(hostname=host, port=port,
username=username, password=password)
print('\033[32m<<< Connected to %s >>>\033[0m' % host)
'''
The -S flag tells sudu to expect the password to come from stdin
The -p '' flag tells sudo to use '' or the empty string as the prompt
for the password.
The permission in Linux is a octal number, but in ini file is decimal.
So use %o to transform decimal to octal.
'''
stdin, stdout, stderr = ssh.exec_command(
"sudo -S -p '' chmod %o %s" % (dirpathmod, remote_dir))
# Enter the sudo password
stdin.write(sudo_password + '\n')
stdin.flush()
except Exception as e:
print('\033[91m<<< CONNECT %s EXCEPTION!! >>>\033[0m %s ' % (host, e))
print()
return host
ssh.close()
return 1
def sftp_upload(host, port, username, password, local_dir, remote_dir, filesmod):
'''
Upload local host's file and folder to remote host by sftp
@ param host: Remote host's IP.
@ param port: Remote host's port for sftp
@ param username: Remote host's login username for ssh.
@ param password: Remote host's login password for ssh.
@ param local: Local host upload folder.
@ param remote: Remote host upload folder.
@ param filesmod: The permission of upload files.
'''
paramiko.util.log_to_file('sftp.log')
try:
'''
Create a new SSH session over an existing socket, or socket-like
object. This only creates the Transport object; it doesn’t begin the
SSH session yet.
'''
t = paramiko.Transport((host, port))
'''
Negotiate an SSH2 session, and optionally verify the server’s host
key and authenticate using a password or private key
'''
t.connect(username=username, password=password)
# Create an SFTP client channel from an open Transport.
sftp = paramiko.SFTPClient.from_transport(t)
print('\033[44m === UPLOAD FILE TO %s START AT %s === \033[0m' %
(host, datetime.datetime.now().strftime('%H:%M:%S')))
for root, dirs, files in os.walk(local_dir):
'''
os.walk will generate the file names in a directory tree by walking
the tree. For each directory in the tree rooted at directory top
(including top itself), it yields a 3-tuple (root, dirs, files).
root: The path to the directory.
dirs: The names of the subdirectories in dirpath (excluding
'.' and '..').
file: The names of the non-directory files in dirpath
For example, if you want upload all files and floders in root to
/html/ in remote host.
root/(local)
├──home/
│ └──note.txt
├──Pictures/
├──Music/
│ └──hello.txt
└──world.txt
'''
for filespath in files:
'''
Combine the path(root) with the file name(files) under the root
become the file path.
ex: /root/home/ + note.txt => /root/home/note.txt
'''
local_file_path = os.path.join(
root, filespath).replace("\\", "/")
'''
Delete the string of local(param) in the file path(local_file).
ex: if local is /root/, /root/home/note.txt => home/note.txt
'''
local_file = local_file_path.replace(local_dir, '')
'''
Combine the remote host path(remote) with local_file become
remote file path.
ex: /html/ + home/note.txt => /www/home/note.txt
'''
remote_file = os.path.join(remote_dir, local_file)
try:
sftp.put(local_file_path, remote_file)
except Exception as e:
'''
An exception will be generated if the remote folder does
not exist. os.path.split will split the path(remote_file)
into (path, file). And use path to create the folder.
ex: /html/home/note.txt => ('/html/home/', 'note.txt')
'''
sftp.mkdir(os.path.split(remote_file)[0])
sftp.put(local_file_path, remote_file)
# sftp.chmod's 2rd parma need provide decimal Linux permission.
sftp.chmod(remote_file, filesmod)
print("upload %s to remote %s" %
(local_file_path, remote_file))
for name in dirs:
'''
If the local folder is empty, the above loop will not create an
empty folder at the remote. This loop will do this.
'''
local_path = os.path.join(root, name)
a = local_path.replace(local_dir, '')
remote_path = os.path.join(remote_dir, a)
try:
sftp.mkdir(remote_path)
print("mkdir path %s" % remote_path)
except Exception as e:
# The exception will raise if the folder already exists.
pass
print('\033[44m === UPLOAD FILE TO %s FINISH AT %s === \033[0m' %
(host, datetime.datetime.now().strftime('%H:%M:%S')))
print()
t.close()
return 1
except Exception as e:
print()
print('\033[91m<<< UPLOAD %s EXCEPTION!! >>>\033[0m %s' % (host, e))
print()
return host
def print_info(msg):
print('\033[93m****************************************\033[0m')
print('\033[93m*{:^38}*\033[0m'.format(msg))
print('\033[93m****************************************\033[0m')
if __name__ == '__main__':
# Read configuration from upload.ini
try:
config = configparser.ConfigParser()
config.read('upload.ini')
address_list = config.get(
'REMOTE', 'ipaddress').replace(' ', '').split(',')
port = int(config.get('REMOTE', 'port'))
username = config.get('REMOTE', 'username')
password = config.get('REMOTE', 'password')
local_dir = config.get('LOCAL', 'dirpath')
remote_dir = config.get('REMOTE', 'dirpath')
dirpathmod = int(config.get('REMOTE', 'dirpathmod'))
filesmod = int(config.get('REMOTE', 'filesmod'))
sudo_password = config.get('REMOTE', 'sudo_password')
except Exception as e:
print('\033[91m<<< Read configuration FAIL! >>>\033[0m', e)
print()
print_info('IMPORT INI SUCCESS.')
# Verify configuration information
print('Read IP address list from INI file:', address_list)
print('Read SSH port from INI file:', port)
print('Read username from INI file:', username)
print('Read password from INI file:', password)
verify = input('Do you want to start uploading files? (YES/no)')
if verify == 'no':
print_info('Byebye!')
else:
print()
print_info('START UPLOAD OPERATION.')
conn_err = [] # connect fail host list
upload_err = [] # upload fail host list
for address in address_list:
conn_res = ssh_chmod(address, port, username, password,
remote_dir, dirpathmod, sudo_password)
if conn_res == 1:
upload_res = sftp_upload(address, port, username, password,
local_dir, remote_dir, filesmod)
if upload_res != 1:
upload_err.append(upload_res)
else:
conn_err.append(conn_res)
print_info('ALL UPLOAD SUCCESS.')
print()
if len(conn_err) != 0:
print('\033[91m<<< CONNECT FAIL HOST LIST >>>\033[0m')
print(conn_err)
print()
if len(upload_err) != 0:
print('\033[91m<<< UPLOAD FAIL HOST LIST >>>\033[0m')
print(upload_err)
4.sftp INI文件,將某些常更換的參數以INI文件方式儲存。
[REMOTE]
ipaddress = 192.168.1.100, 192.168.1.187
port = 22
username = 1234
password = 1234
filename = /home/moxa/smb.conf
; The permission dirpath.
; chmod need enter a decimal
; 755(octal) -> 493(decimal)
; 777(octal) -> 511(decimal)
filesmod = 420
userid=root
usergroup=root
[LOCAL]
filename = ./smb.conf
5.執行程式。
python3 ssh.py
python3 sftp.py
以上為Paramiko程式碼及如何執行,下一篇會提到如何將寫好的python程式碼編譯成可攜帶的執行檔。
以上如有任何問題,歡迎留言與我討論。
留言
張貼留言