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. 认证

  1. Private key files

    c = Connection("root@192.168.9.73", connect_kwargs={'key_filename': '/home/descart/.ssh/id_rsa'})
    
  2. 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 # 交互式输入密码
posted @ 2025-06-09 20:43  少侠来也  阅读(20)  评论(0)    收藏  举报