热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Python全栈系列46fabric从入门到放弃

说明本想用fabric(paramiko的作者开发的),发现新老版本存在较大的不同,所以放弃了。还是用paramikosh

说明

本想用fabric(paramiko的作者开发的),发现新老版本存在较大的不同,所以放弃了。还是用paramiko + sh脚本的方式。


1 远程主机

远程主机已经设置好了免密登录,连接方式为直连或者frp透传。
可以使用一个conf文件记录自己的主机记录(因为使用秘钥,所以里面没有密码,相对还算安全)
machines.conf

[m1]
name = 大机器
host_ip = 111.222.333.444
user = root
port = 111[m2]
name = 小机器
host_ip = 111.222.333.444
user = root
port = 222

配置文件会被读入为有序字典,然后就可以用了。

import DataManipulation as dm
res = dm.read_conf('machines.conf')

2 使用paramiko


2.1 SSH秘钥连接

paramiko可以指定一个路径来读取秘钥。


2.2 远程主机命令/反馈

paramikoe执行命令后会返回stdin, stdout, stderr三项,我们可以只取stdout。


# 执行远程主机命令
def remote_ssh_exe_cmd(ssh_key_fpath=None, machine_conf_fpath=None, machine_code=None, cmd_line=None):# 私钥private_key = paramiko.RSAKey.from_private_key_file(ssh_key_fpath)# 机器配置res = dm.read_conf(machine_conf_fpath)client = paramiko.SSHClient()client.set_missing_host_key_policy(paramiko.AutoAddPolicy())hostname = res[machine_code]['host_ip']username = res[machine_code]['user']port = res[machine_code]['port']try:client.connect(hostname=hostname, port=port,username=username, pkey=private_key)stdin, stdout, stderr = client.exec_command(cmd_line)try:# print('a')cmd_result = stdout.read().decode('utf-8')# print(cmd_result)except:# print('b')cmd_result = Noneexcept:print('remote_ssh_exe_cmd error')cmd_result = Nonefinally:client.close()return cmd_result

一般的调用方法如下:

input_dict = {}
input_dict['ssh_key_fpath'] = '私钥地址'
input_dict['machine_conf_fpath'] = './machines.conf'
input_dict['machine_code'] = 'm1'
input_dict['cmd_line'] = 'df -h'res = remote_ssh_exe_cmd(**input_dict)

3 应用功能

假设现在已经可以python程序执行远程操作了,现在我们来做一些常见操作


3.1 获取远程主机状态


  • 1 主机名

In [24]: input_dict = {}...: input_dict['ssh_key_fpath'] = 'YOURS/.ssh/id_rsa'...: input_dict['machine_conf_fpath'] = './machines.conf'...: input_dict['machine_code'] = 'm1'...: input_dict['cmd_line'] = 'hostname'...:...: print(dm.remote_ssh_exe_cmd(**input_dict)[1])
andy-Z97X-UD3H

  • 2 ip

input_dict['cmd_line'] = '''ifconfig -a'''
print(dm.remote_ssh_exe_cmd(**input_dict)[1])
---

  • 3 内存

In [27]: # 内存...: input_dict['cmd_line'] = '''free -m'''...: print(dm.remote_ssh_exe_cmd(**input_dict)[1])total used free shared buff/cache available
Mem: 32026 6293 15983 213 9749 24910
Swap: 57251 0 57251

  • 4 cpu详情

In [28]: # cpu...: input_dict['cmd_line'] = '''cat /proc/cpuinfo'''...: print(dm.remote_ssh_exe_cmd(**input_dict)[1])
processor : 0
vendor_id : GenuineIntel
cpu family : 6

  • 5 cpu(逻辑)个数

In [29]: # cpu个数...: input_dict['cmd_line'] = '''cat /proc/cpuinfo |grep "processor"|wc -l '''...: print(dm.remote_ssh_exe_cmd(**input_dict)[1])
8

  • 6 磁盘占用

In [30]: # 磁盘占用...: input_dict['cmd_line'] = '''df -h'''...: print(dm.remote_ssh_exe_cmd(**input_dict)[1])
文件系统 容量 已用 可用 已用% 挂载点
udev 16G 0 16G 0% /dev
tmpfs 3.2G 66M 3.1G 3% /run

  • 7 cpu温度

In [31]: # cpu温度...: input_dict['cmd_line'] = '''sensors'''...: print(dm.remote_ssh_exe_cmd(**input_dict)[1])
acpitz-virtual-0
Adapter: Virtual device
temp1: +27.8°C (crit = +105.0°C)
temp2: +29.8°C (crit = +105.0°C)coretemp-isa-0000
Adapter: ISA adapter
Package id 0: +69.0°C (high = +80.0°C, crit = +100.0°C)
Core 0: +67.0°C (high = +80.0°C, crit = +100.0°C)
Core 1: +69.0°C (high = +80.0°C, crit = +100.0°C)
Core 2: +67.0°C (high = +80.0°C, crit = +100.0°C)
Core 3: +65.0°C (high = +80.0°C, crit = +100.0°C)

  • 8 获取资源占用情况(抓取htop的日志,后补)



3.2 获取某个文本文件的内容

在m1主机建立/tmp/test.txt

hello test

在Macbook上(m0)执行

import DataManipulation as dminput_dict = {}
input_dict['ssh_key_fpath'] = 'YOURS/.ssh/id_rsa'
input_dict['machine_conf_fpath'] = './machines.conf'
input_dict['machine_code'] = 'm1'
input_dict['cmd_line'] = 'cat /tmp/test.txt'print(dm.remote_ssh_exe_cmd(**input_dict)[1])
---
In [12]: run test4_file.py
hello test

3.3 为文件建立副本

m0端

# 建立副本
input_dict['cmd_line'] = 'cp /tmp/test.txt /tmp/test.txt.bak_1234'
print(dm.remote_ssh_exe_cmd(**input_dict)[1])

m1端

┌─root@andy-Z97X-UD3H:/tmp
└─ $ ls
test.txt
test.txt.bak_1234
---
┌─root@andy-Z97X-UD3H:/tmp
└─ $ cat test.txt.bak_1234
hello test

3.4 覆盖文件内容

在m0端执行

# 覆盖文件内容
new_content='hello new content'
input_dict['cmd_line'] = 'echo "%s">/tmp/test.txt' %new_content
print(dm.remote_ssh_exe_cmd(**input_dict)[1])

在m1端执行

┌─root@andy-Z97X-UD3H:/tmp
└─ $ cat test.txt
hello new content

3.5 追加写入内容

在m0端执行

## 追加写入内容
new_content = 'hello new content 1'
input_dict['cmd_line'] = 'echo "%s">>/tmp/test.txt' % new_content
print(dm.remote_ssh_exe_cmd(**input_dict)[1])

在m1端执行

┌─root@andy-Z97X-UD3H:/tmp
└─ $ cat test.txt
hello new content
hello new content 1

3.6 只修改某一行文本

m0端执行

## 注释第二行
input_dict['cmd_line'] = 'cat /tmp/test.txt'
# 1.先读取文件。使用换行符分割,末尾会多出一行
original_content = dm.remote_ssh_exe_cmd(**input_dict)[1].split('\n')[:-1]
original_content[1] = '#' + original_content[1]
# 2 再覆盖写回文件
input_dict['cmd_line'] = 'echo "%s">/tmp/test.txt' % '\n'.join(original_content)
print(dm.remote_ssh_exe_cmd(**input_dict)[1])

m1端执行

┌─root@andy-Z97X-UD3H:/tmp
└─ $ cat test.txt
hello new content
#hello new content 1

3.7 传送文件/文件夹


什么是scp?scp是一种基于SSH的协议,可在网络上的主机之间提供文件传输。 使用scp,您可以在主机之间快速传输文件以及基本文件属性,例如访问权限和通过FTP无法可用的时间戳。


文件的传输我使用了scp,比sftp应该更好一些,详情可以参考这篇文章

scp在paramiko里面的支持也有点怪,不想用paramiko实现这个功能。突然发现我似乎也没有必要用paramiko这个包,因为大部分的命令是确知的、简单的命令。

# scp方法从远程获得文件
import os
def scp_get_file_from_remote(machine_conf_fpath = None,machine_code =None, remote_absfpath = None, local_absfpath=None ,mode='file'):assert all([machine_code,remote_absfpath, local_absfpath]),'机器号、远程文件地址和本地文件地址不为空'if mode.lower() =='file':# 如果是文件opt = ''else:opt = '-r'# 从机器配置文件里获取信息并组成用户+主机的前缀machine_conf = dm.read_conf(machine_conf_fpath)## 从远端获取文件到本地remote_port = machine_conf[machine_code]['port']remote_host = machine_conf[machine_code]['user'] +'@'+ machine_conf[machine_code]['host_ip']remote_fpath = remote_host + ':' + remote_absfpathscp_cmd_line = 'scp -P{0} {1} {2} {3}'.format(remote_port, opt, remote_fpath, local_absfpath)return os.system(scp_cmd_line)scp_input_dict = {}
scp_input_dict['machine_conf_fpath'] = './machines.conf'
scp_input_dict['machine_code'] = 'm1'
scp_input_dict['remote_absfpath'] = '/tmp/test.txt'
scp_input_dict['local_absfpath'] = '/YOURPATH/test_xxx1.txt'scp_get_file_from_remote(**scp_input_dict)
---
如果要拷贝文件夹的化,就在入参的地方把mode改为folder(其实不为file就可以)

在这里插入图片描述
从本地传到远端的几乎一样,就不写了。

既然是自建算网,那么我可以设定一台机器作为数据中转中心(NAS)。
我发现反而需要做的只是:为需要通信的主机生成秘钥对,然后分发到对应主机的ssh文件下面就可以了(authorized_keys)。这样任何一台主机需要数据就可以直接获取。

接下来就把这些操作封装到DM里面就可以方便调用了。


4 操作日志

使用sh命令操作是一个很需要小心的事情(一不小心就崩溃了),一方面我们将操作简化和固定化;另一方面需要mongo记录操作日志,使用opr_id作为记号(操作前进行backup,命名为backup_before_opr_xxx),这样万一搞砸了还有一线机会恢复(例如写一个roll_back_by_checkpoint)。

关于python操作mongo的介绍可以看我这篇文章。

以下是个例子,这里用with的方法是为了避免操作打开太多数据库连接(存完数据就释放):

import pymongo
import DataManipulation as dm
import time
with pymongo.MongoClient('mongodb://localhost:27017/') as conn:db = conn['andylog']colt = db['log1']log_dict = {}log_dict['operator'] = 'andy'log_dict['opr_id'] = 'op001'log_dict['create_time'] = dm.get_curdatetime_str()log_dict['event'] = 'cat file'log_dict['cmd_line'] = 'cat /tmp/test.txt'log_dict['ts'] = time.time()colt.insert_one(log_dict)

数据库里的变化:

> db.log1.find()
{ "_id" : ObjectId("5fab5975e1dbe95ebf0444b6"), "operator" : "andy", "opr_id" : "op001", "create_time" : "2020-11-11 11:24:37", "event" : "cat file", "cmd_line" : "cat /tmp/test.txt", "ts" : 1605065077.215517 }
>

关于opr_id:


  • 1 我们希望操作的顺序可以以abc123的方式比较好的存下来,但是如果考虑并发模式的话会比较难。
  • 2 可以加一个session的概念。例如按时间(2020-11-11 11:24:37)、操作类型(cat file)、用户、主机、APP的方式标志一个唯一的session,在每个session里维持操作顺序(由同一个进程/线程管理和存储)

推荐阅读
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • Python如何调用类里面的方法
    本文介绍了在Python中调用同一个类中的方法需要加上self参数,并且规范写法要求每个函数的第一个参数都为self。同时还介绍了如何调用另一个类中的方法。详细内容请阅读剩余部分。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • 移动端常用单位——rem的使用方法和注意事项
    本文介绍了移动端常用的单位rem的使用方法和注意事项,包括px、%、em、vw、vh等其他常用单位的比较。同时还介绍了如何通过JS获取视口宽度并动态调整rem的值,以适应不同设备的屏幕大小。此外,还提到了rem目前在移动端的主流地位。 ... [详细]
author-avatar
书友70030711
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有