fabric
Fabric是 封装了invoke和paramiko两个库
设置utf-8
# 在windows执行命令,输出有中文时乱码
from invoke import runners
runners.default_encoding = lambda: "utf-8"
invoke
命令解析,任务管理,执行本地命令
1.选项
# 可选选项
@task(optional=['log'])
def compile(c, log=None):
if log:
log_file = '/var/log/my.log'
# Value was given, vs just-True
if isinstance(log, unicode):
log_file = log
# Replace w/ your actual log setup...
set_log_destination(log_file)
# Do things that might log here...
$ inv compile --log # log=True
$ inv compile --log=foo.log # log=foo.log
# 迭代选项
@task(iterable=['my_list'])
def mytask(c, my_list):
print(my_list)
$ inv mytask --my-list foo --my-list bar --my-list foo
['foo', 'bar', 'foo']
# incrementable 选项
@task(incrementable=['verbose'])
def mytask(c, verbose=0):
print(verbose)
$ inv mytask --verbose
1
$ inv mytask -v
1
$inv mytask -vvv
3
# Dashed(-在中间) 和下划线
@task
def mytask(c, my_option=False):
pass
$ inv mytask --my-option
# boolean
@task
def run_tests(c, color=True): # 默认值要设为true,使用--no-xx
# ...
inv run_tests --no-color # color=False
2. 任务
# 文件名:tasks.py
from invoke import task
@task
def hello(c):
print("Hello world!")
@task(help={'name': 'A param for test'}) # 添加帮助信息
def greet(c, name):
# 运行shell命令
c.run(f"echo {name} world!")
$ invoke --list # 或 inv -l
$ inv greet --name="hello"
#前置任务与后置任务
@task(pre=[clean], post=[message])
def build(c):
c.run("echo build")
@task(clean, message) # 均视为前置任务
# 前置任务与后置任务参数调用
@task(pre=[call(greet, name="hubble")])
def ask(c):
print("come here")
3.上下文
属性:
c.command_cwds
c.command_prefixes
c.config
c.cwd # 返回当前工作目录
with c.cd('/path/to/app'):
# source /etc/profile && ./manage.py migrate
with c.prefix('source /etc/profile'):
c.run('./manage.py migrate')
c.run('./manage.py loaddata fixture')
4. mock
mc = MockContext(run={'mycommand': Result("mystdout")})
assert mc.run('mycommand').stdout == "mystdout"
mc = MockContext()
mc.set_result_for('run', 'mycommand', Result("mystdout"))
assert mc.run('mycommand').stdout == "mystdout"
paramiko
ssh 功能
import paramiko
# 执行命令
with paramiko.SSHClient() as ssh:
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname='192.168.9.73', port=22, username='root', password='123')
# 执行命令
stdin, stdout, stderr = ssh.exec_command('ls')
# 获取命令结果
result = stdout.read()
import paramiko
# 上传下载
with paramiko.Transport(('hostname',22)) as transport:
transport.connect(username='GSuser',password='123')
sftp = paramiko.SFTPClient.from_transport(transport)
# 将location.py 上传至服务器 /tmp/test.py
sftp.put('/tmp/location.py', '/tmp/test.py')
# 将remove_path 下载到本地 local_path
sftp.get('remove_path', 'local_path')
1. 认证
-
Private key files
c = Connection("root@192.168.9.73", connect_kwargs={'key_filename': '/home/descart/.ssh/id_rsa'}) -
password
c = Connection("root@192.168.9.73", connect_kwargs={'password': '123'})
2. 配置文件
```bash
/etc/fabric.yml
~/.fabric.py
# yml
connect_kwargs:
password: 123
user: root
tasks:
collection_name: fabfile
# py
connect_kwargs={'password': '123'}
user = "root"
```
3. connection
class fabric.connection.Connection(host, user=None, port=None, config=None, gateway=None, forward_agent=None, connect_timeout=None, connect_kwargs=None, inline_ssh_env=None)
with Connection('host') as c:
c.run('command')
c.put('file')
# 方法 forward_local
with Connection('root@192.168.9.82').forward_local(6032, remote_port=9032):
# 访问本地6032端口会被转发到远程的9032上
ret = requests.get("http://localhost:6032/v1/case")
# 方法 forward_remote
c = Connection('root@192.168.9.82')
with c.forward_remote(6032, 9032):
# 远程的6032端口会转发到9032上
c.run("ss -lnp|grep 6032")
c.run("curl -H 'projectid:2' http://localhost:6032/v1/case/")
# get(*args, **kwargs) 获取远程文件
# `put`(local, remote=None, preserve_mode=True) 上传文件
# local(*args, **kwargs) 执行本地命令
# run(command, **kwargs) 执行命令
# sudo(command, **kwargs)
1. run
run(command, **kwargs)
command:str 执行的命令
1. asynchronous:bool
p: Promise = c.run("sleep 5;ls", asynchronous=True)
result: Result = p.join()
2. disown: bool
否认,脱离关系,返回None 只执行命令,不需要任何结果
3. dry: bool
--dry, 只打印命令,不执行
4. echo: bool
先打印命令再执行;hide=True will override
5. echo_stdin:bool
是否把标准输入的内容打印出来
6. encoding
#返回值编码类型
c.run("echo 中文", encoding='utf-8')
7. hide
# 不在终端显示执行的标准输出
hide='out' hide='err', hide='both' or hide=True
8. fallback:bool
控制自动回退功能,默认True
9. err_stream
#默认到sys.stderr
c.run("lsdf", err_stream=open("f.log"))
10. in_stream
c.run("yum install tree", in_stream=open("cmd.txt"))
11. out_stream
12. pty:bool
默认为False, 某些命令在终端下可能会表现不同
将不使用os.environ
#1. TTY
Teletypes, 原来指的是电传打字机,是通过串行线用打印机键盘通过阅读和发送信息的东西,后来这东西被键盘与显示器取代,所以现在叫终端比较合适。
tty1-tty6表示文字界面,可以用Ctrl+Alt+F1-F6切换,+F7就是切换回图形界面。
#2. pty(虚拟终端):
但是如果我们远程telnet到主机或使用xterm时不也需要一个终端交互么?是的,这就是虚拟终端pty(pseudo-tty)。
#3. pts/ptmx(pts/ptmx结合使用,进而实现pty): 在Xwindows模式下的伪终端。
ptmx, pts - pseudoterminal master and slave
# 两个pts间发送消息
echo “Hey” > /dev/pts/2
13. shell: str
默认/bin/bash, COMSPEC or cmd.exe on Windows.
14. timeout
执行命令超时时间,单位秒,
15. warn:bool
不raise UnexpectedExit异常, 忽略命令执行过程中退出码不为0的错
16. watchers: list
from invoke import Responder
responder = Responder(
# pattern=r"Is this ok \[y/N\] ", # 正则
pattern=r"Installed size",
response="y\n",
)
c.run("yum -yremove tree", watchers=[responder])
# 备注
终端执行命令看到的是:
Installed size: 87 k
Is this ok [y/N]:
python执行只显示了Installed size: 87 k,没有Is This ok,所以正则要用第二个
2. Result
r:Result = c.run("ls")
# 属性, 通过dir(r)查看
return_code
ok
failed
stdout
stderr
# 函数
tail(stream, count=10)
r.tail("stdout",5)
3. get
# 下载文件
get(remote, local=None, preserve_mode=True)
4. put
# 上传文件
put(local, remote=None, preserve_mode=True)
5. local
# 执行本地命令,参数同run
local(command)
# env: dict
c.local("env |grep SHELL", env={"SHELL": "/bin/dash"})
# replace_env: bool, 不使用os.environ
6. sudo
sudo(command, **kwargs)
p = c.sudo("id", user="ss", password="123")
4. group
from fabric import SerialGroup, ThreadingGroup
group = SerialGroup(
"host1", "host2", "host3", user="admin", forward_agent=True,
) # group是Connection列表
group.run("ls")
# ThreadingGroup使用多线程执行命令
config = {
'host1': {'password': '...'},
'host2': {'password': '...'},
}
connections = []
for hostname, parameters in config.items():
conn = Connection(host=hostname, connect_kwargs=parameters)
connections.append(conn)
try:
command = f"{interpreter} << EOF\n{task.get('content')}\nEOF"
# SerialGroup是串行
with ThreadingGroup.from_connections(connections) as group:
result = group.run(command, warn=True, timeout=timeout)
except GroupException as err:
result = err.result
for conn, conn_result in result.items():
user = conn.user
ip = conn.host
if isinstance(conn_result, CommandTimedOut):
continue
print(conn, conn_result)
5. 任务
# 文件名为fabfile
$fab -l
$fab -H 192.168.9.73 node1 任务名 # 密码等信息在配置文件中
$fab -H wick@192.168.9.6 任务名 --prompt-for-login-password # 交互式输入密码


浙公网安备 33010602011771号