python使用fabric之只看这篇就够了

fabric 是什么

项目地址在: https://gitee.com/xiaofeipapa/fabric-tutorial

建议先下载代码, 边看边运行.

fabric 是python的一个组件, 能够在远程服务器运行你写好的脚本, 也就是说这是一个运维自动化工具.

以前运维人员在远程服务器上安装软件, 要先登录 -> 写脚本(如果习惯本地写程序, 那么还要上传程序到服务器) -> 针对服务器反馈输入数据 -> 调试 ..

如果用fabric, 这个流程就是全部自动化了. fabric的程序开发流程一般是这样:

  1. 先在远程服务器试验你的脚本/想法.
  2. 用python fabric 实现它们.
  3. 运行结果, 观察是否正确.

如果只需要操作一两台服务器, 那么手动操作肯定比写程序快. 如果要操作几十上百台机器, 那么当然是写好程序, 然后让他们自动在几十台服务器上面跑更方便. (程序员就是要学会偷懒! )

2021版教程说明

网上能找到的fabric 大多东摘西抄, 程序不能运行. 锅主要由fabric 来背, 一开始他们并不支持 python3, 所以当python3 流行之后, 以前针对 python2 的fabric教程就基本不能用了; 于是有热心程序员们搞了个非官方的fabric3 , 写了一些教程(很少且很多文章是错的), 但后来fabric 官方支持了 python3 , 也出了个教程....

于是在互联网上至少存在3种fabric 教程. 针对python2的, 针对非官方python3版本的, 还有官方python3 官方版本的, 初学者大概会被弄得要发疯了吧....

所以我将这份教程命名为2021版, 表示这是今年最新的版本. 同时这是针对官方python3版本的教材, 如果你不熟悉fabric, 就不用看网上其他教程了, 看这一篇即可.

最后, 这篇教程用的是 python3 的fabric 版本. 因此你本机要先安装python3. 如果是linux 版本, 一般是这样:

sudo apt install python3-pip

安装完成之后, 只要用:

sudo pip3 install xxx

所安装的包/组件就都是python3版本了.

如果是windows系统, 假设你下载安装的就是python3的软件, 那么当你使用 pip install 的时候, 这个pip 就是3的版本. 所以不需要再多设置.

如何安装

linux:

sudo pip3 install fabric

windows:

pip install fabric

实验环境

如果你很熟悉docker 或者自己就有云服务器, 那么试验环境就有了. 如果你没有(特别是windows 用户, 很难装好docker) 怎么办.

你可以使用我的这个虚拟机镜像. 首先你要安装 virtualbox, 下载地址在这里: https://www.virtualbox.org/wiki/Downloads

在某云盘下载我的这个虚拟机镜像:
这是个 xubuntu 系统(ubuntu的变种), 我最爱的系统之一.

安装之后用virtualbox 把它导入, 一步步操作如下:

打开virtualbox之后, 选择New


image.png

Type选择Linux, Version 选择Ubuntu(64bit) , 然后点击Next.

image.png

内存默认 1024 就够了, 点Next

image.png

选择"Use an existing ... " , 然后点右边那个文件夹图标

在打开的界面中, 选择 Add


image.png

然后选择刚才下载的xub.vdi文件, 如图:


image.png

然后点 choose


image.png

这时候你会看到 create 已经变得可选了, 点它. 就会看到虚拟机已经成功创建.

image.png

点击右上方的Settings, 然后在弹出的界面设置网卡:


image.png

(中文名字应该是桥接网络) . 这样选了之后, 你的电脑(称为宿主机) 就可以访问虚拟机里的操作系统了.

双击xub 启动系统, 你会看到 xubuntu的界面. 这个系统有两个用户的信息, 分别是:
xiaofeipapa / 密码 aa
root / 密码 test

在xubuntu的桌面点击鼠标右键, 选择 Open Terminal Here, 打开命令行, 输入:

ifconfig 

查看自己的ip是多少. 在我这里是 192.168.135.24 .

然后在宿主机里打开命令行, 输入:

ssh root@192.168.135.24 

然后输入root的密码(test ) , 就可以看到ssh 登录成功了. 好了, 你拥有一个实验环境, 可以在上面试验fabric了.

实验环境信息总结

ip : 192.168.135.24 ( 也许你的机器会不同, 在虚拟机里用ifconfig来查看)
用户: root / test

fabric 基础

以下程序的参数都针对我那个虚拟机镜像, 如果你有自己的虚拟机或者云服务器, 改相应参数即可.

起步: 连接服务器, 执行命令

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
  作者: 小肥爬爬
  简书: https://www.jianshu.com/u/db796a501972
  gitee: https://gitee.com/xiaofeipapa/python-toolkit
  您可以自由转载此博客文章, 恳请保留原链接, 谢谢!
"""
from fabric import Connection


def do_it():

    host = '192.168.135.24'
    user = 'root'
    password = 'test'

    # ssh 连接的正确姿势
    conn = Connection(host=host, user=user, connect_kwargs={'password': password})

    # 在远程机器运行命令(用run方法), 并获得返回结果
    # hide 表示隐藏远程机器在控制台的输出, 达到静默的效果
    # 默认 warn是False, 如果远程机器运行命令出错, 那么本地会抛出异常堆栈. 设为True 则不显示这堆栈.
    cmd = 'ls /tmp'
    result = conn.run(cmd, hide=True, warn=True, encoding='utf-8')

    # 正常运行时, 信息在 stdout里
    print('-------- 下面是 stdout 信息')
    print(result.stdout.strip())

    # 出错时, 信息在 stderr 里
    print('-------- 下面是 stderr 信息')
    print(result.stderr.strip())


if __name__ == '__main__':
    do_it()

注释里已经把api用法说得很清楚了. 请阅读并运行程序加深印象.

封装工具类 fab_utils.py

在我们写程序的时候, 其实大部分时候都运行正常. 如果每次run之后都去判断 stdout, stderr ... 代码很快变得混乱不堪. 所以我的选择是将之封装成方法, 使之更加轻便. 这个文件命名为 fab_utils.py, 内容如下:

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
  作者: 小肥爬爬
  简书: https://www.jianshu.com/u/db796a501972
  gitee: https://gitee.com/xiaofeipapa/python-toolkit
  您可以自由转载此博客文章, 恳请保留原链接, 谢谢!

  将fabric api 再封装一层, 使之更好用
"""


class FabException(Exception):
    def __init__(self, msg):
        Exception.__init__(self, msg)


# 运行远程命令
def run(conn, cmd, hide=True, warn=True):

    r = conn.run(cmd, encoding='utf8', hide=hide, warn=warn)

    result, err = r.stdout.strip(), r.stderr.strip()
    if err:
        raise FabException(err)

    return result

以后, 我会把这个工具库逐渐扩大, 加入更多有用的方法. 经过这种方式, fabric的大多数功能就变成你的工具库, 可以轻松写出大多数的运维程序.

写一个程序 s2_test_fab_utils.py 来测试:

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
  作者: 小肥爬爬
  简书: https://www.jianshu.com/u/db796a501972
  gitee: https://gitee.com/xiaofeipapa/python-toolkit
  您可以自由转载此博客文章, 恳请保留原链接, 谢谢!
"""
from fabric import Connection
import fab_utils


def do_it():

    host = '192.168.135.24'
    user = 'root'
    password = 'test'

    # ssh 连接的正确姿势
    conn = Connection(host=host, user=user, connect_kwargs={'password': password})

    # 结果变得更简单了
    cmd = 'ls /tmp'
    result = fab_utils.run(conn, cmd)

    # 正常运行时, 信息在 stdout里
    print('-------- 下面是结果')
    print(result)

    # 出错时, 程序会抛出异常
    # 来一个出错的例子, tmp/xiaofeipapa 目录不存在
    cmd = 'ls /tmp/xiaofeipapa'
    result = fab_utils.run(conn, cmd)
    print(result)


if __name__ == '__main__':
    do_it()

运行这个结果, 你能看到第一个例子成功输出结果, 第二个例子抛出异常, 这就是我想要的效果.

ssh无密码登录ssh

登录密码写在参数里当然不安全. 用linux 系统的同学都知道, 可以配置无密码方式访问服务器, 避免暴露密码明文在参数里. (windows我暂时不知道怎么办, 有待请教. )

首先在宿主机生成密钥, 一路按回车:

ssh-keygen -t rsa

然后运行这个脚本:

ssh-copy-id root@192.168.135.24

输入root的登录密码 test, 以后就可以无密码访问服务器了. 相应地, fabric的连接变成如下:

conn = Connection(host=host, user=user)

不用输入密码了.

运行自定义shell脚本

像ls, cd 都是bash 预先提供的命令. 如果要运行自己写的shell怎么办? 有两个办法: 如果sh 文件很大, 那么先在本地写好, 然后传到服务器上运行. 如果shell很少, 直接用这个方式运行:

conn.client.exec_command(cmd)

下面的这个例子演示了如何运行短的shell脚本:

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
  作者: 小肥爬爬
  简书: https://www.jianshu.com/u/db796a501972
  gitee: https://gitee.com/xiaofeipapa/python-toolkit
  您可以自由转载此博客文章, 恳请保留原链接, 谢谢!
"""
from fabric import Connection
import fab_utils


def do_it():

    host = '192.168.0.12'
    user = 'root'

    # ssh 连接的正确姿势
    conn = Connection(host=host, user=user)

    # 运行shell之前, 要随便运行一个命令, 获得运行环境
    fab_utils.run(conn, 'uname -a')

    # 运行shell脚本
    # 在shell里 [ -d xxx ] 表示检查文件夹是否存在
    # [ -d xxx ] 表示检查文件是否存在
    cmd = '[ -d /tmp ] && echo ok'
    _stdin, _stdout, _stderr = conn.client.exec_command(cmd)
    result = _stdout.read().strip().decode('utf8')

    print('--- std out: ')
    print(result)


if __name__ == '__main__':
    do_it()


检查文件/夹是否存在

bash没有提供检查文件夹是否存在, 文件是否存在的方法, 而这两个方法是经常需要使用的. 所以我们可以用简单的shell脚本来封装这两个方法. 在 fab_utils.py 里加入如下方法:


# 检查文件夹是否存在
def is_dir_exists(conn, dir_path):
    cmd = '[ -d ' + dir_path + ' ] && echo ok'
    _stdin, _stdout, _stderr = conn.client.exec_command(cmd)
    result = _stdout.read().strip().decode('utf8')

    return result == 'ok'


# 检查文件文件是否存在
def is_file_exists(conn, file_path):
    cmd = '[ -f ' + file_path + ' ] && echo ok'
    _stdin, _stdout, _stderr = conn.client.exec_command(cmd)
    result = _stdout.read().strip().decode('utf8')

    return result == 'ok'


# 判断是否有该文件夹, 不存在时可以通过参数控制生成文件夹
def check_has_dir(conn, dir_path, create_if_not_exist=True):

    if not is_dir_exists(conn, dir_path):
        if create_if_not_exist:
            run(conn, 'mkdir -p ' + dir_path)


# 判断是否有该文件, 不存在时可以通过参数控制生成文件夹
def check_has_file(conn, file_path, create_if_not_exist=True):

    if not is_dir_exists(conn, file_path):
        if create_if_not_exist:
            run(conn, 'touch ' + file_path)

然后写一个python文件来检查:

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
  作者: 小肥爬爬
  简书: https://www.jianshu.com/u/db796a501972
  gitee: https://gitee.com/xiaofeipapa/python-toolkit
  您可以自由转载此博客文章, 恳请保留原链接, 谢谢!
"""
from fabric import Connection
import fab_utils
import os, os.path


def do_it():

    host = '192.168.0.12'
    user = 'root'

    # ssh 连接的正确姿势
    conn = Connection(host=host, user=user)

    # 运行shell之前, 要随便运行一个命令, 获得运行环境
    fab_utils.run(conn, 'uname -a')

    # 检查文件夹
    dir_path = '/root/data'
    fab_utils.check_has_dir(conn, dir_path)

    # 查看创建文件夹结果
    result = fab_utils.run(conn, 'ls ' + dir_path)
    print(result)

    # 检查文件
    file_path = os.path.join(dir_path, 'done.txt')
    fab_utils.check_has_file(conn, file_path)

    # 查看创建文件结果
    result = fab_utils.run(conn, 'ls ' + file_path)
    print(result)


if __name__ == '__main__':
    do_it()

安全的删除

我们都知道在服务器上尽量不要用rm命令, 否则删错文件那就完了. 所以在用fabric的时候我们同样要注意这个问题. 在fab_utils.py 里加入这个方法:

# 在服务器安全删除文件/文件夹的方法
def safe_rm(conn, file_path):

    # 先检查是否存在
    try:
        run(conn, 'ls ' + file_path)
    except FabException:
        print(file_path + ' 不存在')
        return

    # 在/tmp下生成当天目录
    result = run(conn, 'date +%Y-%m-%d')

    trash_dir = '/tmp/my_trash/' + result
    check_has_dir(conn, trash_dir)

    # 在当天下生成uuid, 确保mv 不会失败
    result = run(conn, 'uuidgen')
    trash_dir = trash_dir + '/' + result
    check_has_dir(conn, trash_dir)

    run(conn, 'mv ' + file_path + ' ' + trash_dir)

写一个文件来测试. 程序在 s5_rm_test.py

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
  作者: 小肥爬爬
  简书: https://www.jianshu.com/u/db796a501972
  gitee: https://gitee.com/xiaofeipapa/python-toolkit
  您可以自由转载此博客文章, 恳请保留原链接, 谢谢!
"""
from fabric import Connection
import fab_utils
import os, os.path


def do_it():

    host = '192.168.0.12'
    user = 'root'

    # ssh 连接的正确姿势
    conn = Connection(host=host, user=user)

    # 运行shell之前, 要随便运行一个命令, 获得运行环境
    fab_utils.run(conn, 'uname -a')

    # 删除之前创建的文件夹 /root/data
    dir_path = '/root/data'
    fab_utils.safe_rm(conn, dir_path)

    if fab_utils.is_dir_exists(conn, dir_path):
        print('--- 文件夹还在')
    else:
        print('--- 文件夹已经删除')


if __name__ == '__main__':
    do_it()

写入文本文件

在服务器上我们通常会有写文件的任务, 例如写入环境变量到配置文件, 在fabric 里可以用简单的shell命令来完成:

conn.run("echo 'hello' >> /tmp/test.txt")

这个命令又是双引号又是单引号, 所以还是封装起来更好用. 在fab_utils.py 里加入如下方法:

# 追加一行到文件末尾
def append_line(conn, file_path, content):
    if not is_file_exists(conn, file_path):
        return

    cmd = "echo '" + content + "' >> " + file_path
    run(conn, cmd)

然后写一个文件来测试, 代码在 s6_env_var.py

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
  作者: 小肥爬爬
  简书: https://www.jianshu.com/u/db796a501972
  gitee: https://gitee.com/xiaofeipapa/python-toolkit
  您可以自由转载此博客文章, 恳请保留原链接, 谢谢!
"""
from fabric import Connection
import fab_utils


def do_it():

    host = '192.168.0.12'
    user = 'root'

    # ssh 连接的正确姿势
    conn = Connection(host=host, user=user)

    # 运行shell之前, 要随便运行一个命令, 获得运行环境
    fab_utils.run(conn, 'uname -a')

    # 在/tmp 下生成 test_rc 文件,
    file_path = '/root/test_rc'
    fab_utils.check_has_file(conn, file_path)

    # 写入内容到文件里

    fab_utils.append_line(conn, file_path, "#==其他配置信息1")
    fab_utils.append_line(conn, file_path, "#==其他配置信息2")
    fab_utils.append_line(conn, file_path, "#==其他配置信息3")
    fab_utils.append_line(conn, file_path, "\n")
    fab_utils.append_line(conn, file_path, "#==JAVA安装信息")
    fab_utils.append_line(conn, file_path, "JAVA_HOME=XXX")
    fab_utils.append_line(conn, file_path, "CLASS_PATH=YYY")
    fab_utils.append_line(conn, file_path, "export PATH=$PATH:JAVA_HOME/bin")

    # 查看文件内容
    result = fab_utils.run(conn, 'cat ' + file_path)
    print(result)


if __name__ == '__main__':
    do_it()

自动响应

有些命令如 passwd , adduser 会等候你继续输入下一步. 像这种需要手动响应的自动化程序应该怎么写? 在fabric 里这样做:


from invoke import Responder

# 其他代码
resp1 = Responder(
        pattern=r'Enter new UNIX password:',
        response=pwd + '\n'
    )

    resp2 = Responder(
        pattern=r'Retype new UNIX password:',
        response=pwd + '\n'
    )

    conn.run('passwd ' + username, pty=True, hide=True, watchers=[resp1, resp2])

Responder 是 invoke 框架的一部分(fabric 已经包括invoke框架) . 它的使用方法如下:

  1. pattern: 正则表达式, 用来匹配命令行的输出. 如果里面有需要转义的字符, 千万要记得转义.
  2. response: 需要输入的内容.
    这两个参数结合起来的意思是: 如果匹配到命令输出, 需要输入信息的时候, 就自动输入response定好的字符串.

如果不确定某些命令的pattern和resonse 是什么. 可以先在服务器手动输入命令看看结果, 然后再写程序. 如果程序多次需要输入, 那么就要写多个responder, 用个数组包起来.

注意: 如果程序长时间没有反馈, 证明你的pattern 写得有问题.

最常见的原因就是转义字符没有进行处理. 下面这是一个例子:


# 在yum上安装软件
def yum_install(conn, cmd):

    resp = Responder(
        # pattern=r'Is this ok [y/d/N]:',   # 错误例子: 没有正则转义
        pattern=r'Is this ok \[y/d/N\]:',
        response='y\n'
    )

    conn.run(cmd, pty=True, watchers=[resp], hide=True)

下面是一个完整的例子: 我们将用户xiaofeipapa的密码改为 bb, 整个过程是自动化无人工干预的:

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""
  作者: 小肥爬爬
  简书: https://www.jianshu.com/u/db796a501972
  gitee: https://gitee.com/xiaofeipapa/python-toolkit
  您可以自由转载此博客文章, 恳请保留原链接, 谢谢!
"""
from fabric import Connection
import fab_utils
from invoke import Responder


def do_it():

    host = '192.168.0.12'
    user = 'root'

    # ssh 连接的正确姿势
    conn = Connection(host=host, user=user)

    # 运行shell之前, 要随便运行一个命令, 获得运行环境
    fab_utils.run(conn, 'uname -a')

    # 将密码改成bb
    username = 'xiaofeipapa'
    pwd = 'bb'
    resp1 = Responder(
        pattern=r'Enter new UNIX password:',
        response=pwd + '\n'
    )

    resp2 = Responder(
        pattern=r'Retype new UNIX password:',
        response=pwd + '\n'
    )

    conn.run('passwd ' + username, pty=True, hide=True, watchers=[resp1, resp2])


if __name__ == '__main__':
    do_it()

fabric 实战案例: 在远程服务器上安装 jdk环境

前面零零散散地介绍了很多fabric的用法, 现在用这个案例把它综合起来: 在远程服务器(虚拟机) 里安装jdk环境.

这个任务做开发/运维的同学应该都不陌生. java技术栈的运维架构通常是1个nginx, 后端挂若干个安装jdk服务器, 以此达到负载均衡的目的.它的任务可以分解为:

  1. 在远程服务器上下载解压jdk包.
  2. 在配置文件写入若干个环境变量(如JAVA_HOME, CLASSPATH) , 指向jdk的解压路径.
  3. 创建新的登录用户, 开发者用它来登录机器, 发布jar 包.

当然其中还包括一些弯弯绕绕的必要任务. 例如怎么判断任务已经运行过, 所以不会再重复运行? 怎么判断jdk包已经存在, 不用再重新下载? 怎么判断环境变量已经写入到配置文件, 不用重复写入?

这些技巧一一列举如下.

怎么知道任务是否第一次执行?

思路: 给任务一个名字, 例如 install_jdk. 第一次在目标机器运行之后, 就会在 /root 目录下生成这个文件: /root/data/install_jdk/done.txt . 所以程序可以判断这个文件是否存在, 如果不存在表示此任务还没执行过. 如果存在, 那么可以通过命令行参数来指定是否再次运行任务.

代码如下:


    def _prepare(self, rerun):
        """
        检查任务是否已经启动过
        :return:
        """
        # 必须要先run连接远程服务器, 否则 conn.client.exec_command 会报错
        fab_utils.run(self.conn, 'ls /tmp')

        # 生成 /root/data 目录
        base_dir = '/root/data'
        fab_utils.create_if_remote_dir_not_exist(self.conn, base_dir)

        # 检查该task 是否已经执行过. 如果已经执行过, 在 /root/data/task 目录下有 done.txt 文件
        task_home = base_dir + '/' + self.task
        fab_utils.create_if_remote_dir_not_exist(self.conn, task_home)
        self.task_home = task_home

        task_file = task_home + '/done.txt'
        if not fab_utils.is_remote_file_exist(self.conn, task_file):
            return

        # 如果存在, 又不指定重新运行, 则退出
        if not rerun:
            title('此程序已经在目标机器运行过了. 如果想再次运行, 请设置启动参数 -r true')
            exit(0)

# ...... 其他代码
# 命令行指定启动参数

if __name__ == '__main__':

    import argparse

    parser = argparse.ArgumentParser('传入参数:***.py')
    parser.add_argument('-r', '--rerun', default=True)    # 开发时用
    # parser.add_argument('-r', '--rerun', default=False)

    args = parser.parse_args()

    rerun = args.rerun

    print('---- 参数 rerun: ', rerun)

    do_it(args)

判断jdk存在,不用重复下载

思路: 当第一次下载好jdk包的时候, 我们可以拿到该包的md5, 然后如果服务器上已经存在jdk包并且md5一致, 那么就不用再下载. 否则开始下载jdk.

代码如下:


        with self.conn.cd(self.task_home):

            # 检查下载jdk
            file_path = self.task_home + '/' + jdk_file
            file_md5 = 'ef599e322eee42f6769991dd3e3b1a31'
            down_link = 'wget https://repo.huaweicloud.com/java/jdk/8u181-b13/' + jdk_file
            fab_utils.check_download(self.conn, file_path, file_md5, down_link)

            # 创建jvm目录
            fab_utils.create_if_remote_dir_not_exist(self.conn, jvm_dir)

            # 解压. 华为的这个包解压后名字不同, 所以手动指定一个目录
            hint('正在解压 jdk ......')
            fab_utils.run(self.conn, 'tar xvf ' + jdk_file + ' -C ' + jvm_dir + '  --strip-components 1 ')

写入配置文件与重复检查

有两个思路:

  1. 利用sed, 如果发现已经有相应的环境变量, 将之删除重新写入.
  2. 用 echo 显示 JAVA_HOME, 如果有值

第一个思路容易错删其他内容, 所以后来采用了第二个思路. 代码如下:


        run_result = conn.run('echo $JAVA_HOME ', hide=True).stdout.strip()
        if run_result == jvm_dir:
            hint(system_file + ' 已经包含 JAVA_HOME, 不需要重新设置')
        else:
            # 追加内容到系统配置
            conn.run("echo '# ===== java 配置' >> " + system_file)
            conn.run("echo 'export JAVA_HOME=" + jvm_dir + "' >> " + system_file)

            # 将 exporter jar 也加入到 classpath 里
            export = 'export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/jre/lib/rt.jar:'\
                     + exporter_jar_path
            conn.run("echo '" + export + "' >> " + system_file)
            conn.run("echo 'export PATH=$PATH:$JAVA_HOME/bin' >> " + system_file)

            # 使环境变量生效
            conn.run('source ' + system_file)

这里还有个小窍门, linux 那么多配置文件如 /etc/profile , /etc/bashrc , 究竟应该写到哪个文件? 查了一下资料, /etc/profile 只在用户第一次登录的时候起作用, 所以应该写到 /etc/bashrc 里.

怎么生成新用户

思路: 生成新用户很简单, 需要考虑的是给它设定密码. 考察了一下linux上生成密码的方式, 最后决定用 pwgen . 用法:

# 生成10个字符的密码, 个数为1
sudo apt install pwgen 
pwgen -s 10 1

代码大概长这样:

    def add_user(self, username):
        """
        增加用户, 并设置密码.
        :param username: 用户名
        :return: 登录密码
        """

        if self._is_user_exist(username):
            title('用户 ' + username + ' 已经存在')
            fab_utils.close_connection(self.conn)
            return None

        # 生成用户. 该命令只在centos 测试过
        fab_utils.run(self.conn, 'adduser ' + username)

        # 生成10位密码
        cmd = 'pwgen -s 10 1'
        password = fab_utils.run(self.conn, cmd)

        # 更改密码
        fab_utils.yum_passwd(self.conn, username, password)

        # 退出登录
        fab_utils.close_connection(self.conn)

        # 登录进行测试
        self.verify_login(username, self.host, password)

        return password

完整代码

项目地址: https://gitee.com/xiaofeipapa/fabric-tutorial

请看 setup_java.py 文件

总结

通过这么多方法和整个案例的展示, 你应该掌握了fabric 90%的用法, 剩下的就靠你自己探索了. 希望你用fabric玩得愉快~

记得给个star和赞啊, 少年~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容