Python 是一种动态的高级编程语言,语法非常简洁,初学者很容易上手。Python 语言表达力非常强大,三两行代码即可完成其他编程语言可能要写几十上百行的功能,开发效率非常高。因此,它经常作为胶水式语言,在自动化运维等开发领域大显身手。
- 语法简洁,易于学习
- 表达力强大,开发效率高
- 执行效率不高,不擅长大项目
Python能做什么
Python 号称自动化运维领域的编程语言,常见的应用场景均能胜任:
- 采集性能数据,比如调用 psutils 采集系统和进程指标;
- 发送数据报表;
- 调用 xlrd 和 xlwt 生成或处理 Excel 报表;
- 调用 matplotlib 生成统计图表,例如折线图、直方图、饼图、热力图……
- 调用 smtplib 发送邮件报告;
- 自动执行命令;
- 调用 pexpect 自动执行交互式命令,支持批量执行;
- 调用 paramiko 登录 SSH 服务器,执行命令或拷贝文件( scp );
- 调用 ansible 在主机集群上执行 playbook ,类似 ansible 的工具还有 fabric ;
- 开发 Web 接口,利用 fastapi 、flask 等框架实现取数接口;
- etc
安装第三方包
Python 之所以这么强大,很大程度上得益于它极其丰富的第三方包生态。不管您想做什么,基本都要现成的包!我们直接把包安装好,然后调用就是了。举个例子,想要请求 HTTP 接口,我们可以使用 requests 包。
那么,怎么安装 Python 第三方包呢?答案是 pip 命令,以安装 requests 包为例:
调用接口
运维攻城狮经常需要跟各种接口打交道,比如 拉取 或者 提交 一些数据,其中又以 HTTP 接口最为常见。那么,如何用 Python 来调用 HTTP 接口呢?
标准库
Python 内置的标准库,提供了 urllib 包,可以用来发起 HTTP 请求。这是一个简单的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import json
import urllib.request
# 待请求URL
url = 'https://fasionchan.com/data/books.json'
# 发起请求,默认为GET
with urllib.request.urlopen(url) as r:
# 读服务器返回的数据,这个接口为json数据
body = r.read(102400)
# 解析json数据
books = json.loads(body)
# 数据为一份书单,逐一遍历并输出书名
for book in books:
print(book['name'])
|
这个段代码请求一个 HTTP 接口,获得 JSON 书单数据,然后遍历打印书名。
requests
与其他编程语言相比,urllib 包调用 HTTP 接口的代码已经足够简洁,但这对 Python 来说还远远不够。
在 HTTP 领域,有一个杀手锏级别的第三方包 requests 必须强烈推荐!引入 requests 后,仅仅一行代码即可完成 HTTP 请求,这是一个等价的例子:
1
2
3
4
5
6
7
8
9
10
11
|
import requests
# 待请求URL
url = 'https://fasionchan.com/data/books.json'
# 发起GET请求,并解析服务器返回的json数据
books = requests.get(url).json()
# 数据为一份书单,逐一遍历并输出书名
for book in books:
print(book['name'])
|
执行命令
运维免不了要经常跟命令打交道,运维攻城狮通常写 Shell 脚本来执行命令,不过 Python 也可以。Python 标准库中的 subprocess 包,就是用来执行命令的。
简单执行
调用 subprocess 包中的 run 函数,一行代码即可执行指定命令:
1
2
3
4
5
|
import subprocess
# 执行:ls -l
p = subprocess.run(['ls', '-l'])
print('returncode:', p.returncode)
|
传给 run 函数的参数是一个字符串,或者一个列表,用来描述要执行的命令,以及执行参数。例子中传的是列表,第一项是命令名 ls ,第二项是 ls 的 -l 参数。这个列表相当于 main 函数中的 argv 数组。
处理命令输出
如果想要获取命令输出的内容,我们可以创建一个管道,作为命令的标准输出,然后从管道读取命令输出的内容。这个例子我们执行 uptime 命令获取系统的平均负载:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import subprocess
with subprocess.Popen(['uptime'], stdout=subprocess.PIPE) as p:
output = p.stdout.read(1024).decode()
# 19:40:05 up 664 days, 5:52, 1 user, load average: 0.00, 0.03, 0.00
values = [
float(v)
for v in output.split('load average:')[1].split(',') # ['0.00', '0.03', '0.00']
]
kvs = zip(
('w1', 'w5', 'w15'),
values,
) # [('w1', 0.00), ('w5', 0.03), ('w15', 0.00)]
print(dict(kvs))
|
SSH
运维攻城狮也经常要通过 SSH 连接到服务器上执行命令、拷贝文件,Python 也可以帮我们做到!同样,我们站在巨人的肩膀上—— paramiko 包,这样安装:
执行命令
装好 paramiko 包后,引入 SSHClient 类,即可大展身手了!这是一个简单的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
from paramiko.client import (
SSHClient,
)
# 创建连接对象
client = SSHClient()
# 加载主机指纹,以防伪装
# 默认从~/.ssh/known_hosts文件加载,也通过传参自定义
client.load_system_host_keys()
# 连接到服务器,密码通过password参数指定
# 也可通过SSH密钥进行认证,默认从~/.ssh/加载密钥,参数也支持指定密钥文件
client.connect('10.128.4.19', username='root')
# 执行命令,返回标准输入、标准输出、以及标准错误输出的读写对象
# - 写到stdin的数据,将送到服务器作为命令的标准输入
# - 而命令的标准输出可以通过stdout读取,标准错误输出可以通过stderr读取
stdin, stdout, stderr = client.exec_command('uptime')
if stderr:
# 处理数据
pass
# 读取命令输出内容,并打印到屏幕
print(stdout.read(10240).decode())
|
拷贝文件
paramiko 也提供了 SFTP 功能,可以用来上传或下载文件。不过,运维人员可能更喜欢用 scp 协议。没事,装个 scp 包即可:
现在,我们先用 paramiko 来建立 SSH 连接;再通过 scp 包来拷贝文件。例子如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
from paramiko.client import (
SSHClient,
)
from scp import (
SCPClient,
)
# 连接到SSH服务器
ssh = SSHClient()
ssh.load_system_host_keys()
ssh.connect('10.128.4.19', username='root')
# 初始化SSH客户端
client = SCPClient(ssh.get_transport())
# 上传文件,当前目录下的test.txt文件上传到服务器/tmp目录下
client.put('test.txt', '/tmp')
# 下载文件,将服务器文件/tmp/test.txt下载到本地,保存为test2.txt
client.get('/tmp/test.txt', 'test2.txt')
|
操作数据库
开发经常会提一些数据维护需求给运维或 DBA ,因此数据库操作能力不可或缺。借助第三方包,Python 可以操作各种数据库。以 MySQL 为例,我们可以用 pymysql 包:
我们来看一个简单的例子,这段代码连到 MySQL 数据库执行 SQL 语句查询数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
import pymysql.cursors
# 连接到MySQL数据库
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='demos',
# 游标类决定数据返回格式
cursorclass=pymysql.cursors.DictCursor,
)
# 连接对象是一个上下文管理器
# 执行with内的代码前,Python会执行连接对象的初始化方法
# 执行with内的代码后,Python会执行连接对象的销毁方法
with conn:
# 获取一个游标对象
with conn.cursor() as cursor:
# 准备要执行的SQL语句
sql = 'select * from kvs'
# 通过游标对象执行SQL语句
cursor.execute(sql)
# 获取执行结果
records = cursor.fetchall()
# 遍历并数据每条记录
for record in records:
print('{}: {}'.format(record.get('key'), record.get('value')))
|
注意到,连接数据库时传了 cursorclass 参数指定连接类,它将决定数据的返回格式,默认为列表。举个例子,假设我们有一张键值对表 kvs ,它有 id 、key 和 value 三个字段:
id |
key |
value |
1 |
one |
apple |
2 |
two |
banana |
3 |
three |
cat |
当我们执行上述 SQL 语句时,pymysql 默认返回三条数据,每条都是一个列表:
1
2
3
4
5
|
[
[1, "one", "apple"],
[2, "two", "banana"],
[3, "three", "cat"]
]
|
如果将游标类指定为 DictCursor ,那么返回的数据每条都是一个字典( dict ):
1
2
3
4
5
|
[
{"id": 1, "key": "one", "value": "apple"},
{"id": 2, "key": "two", "value": "banana"},
{"id": 3, "key": "three", "value": "cat"}
]
|
【小菜学Python】系列文章首发于公众号【小菜学编程】,敬请关注: