python自动化运维常用模块,python 自动化运维框架-程序员宅基地

技术标签: python  

大家好,小编为大家解答python自动化运维需要掌握的技能的问题。很多人还不知道python自动化运维快速入门 pdf,现在让我们一起来看看吧!

Source code download: 本文相关源码

Python自动化运维常用模块

Python telnetlib模块

基本原理

在介绍 telnetlib 中各个 read 函数的意义前,首先了解 telnetlib 的工作原理。

telnetlib 首先通过 socket 连接从网络接收数据,把数据存储到自己的 raw queue 中,然后对其进行(telnet 协议相关的)处理(cook)python用turtle画三角形。处理结果存放在 cooked queue 中供应用程序取用。整个过程如下图所示:
在这里插入图片描述

read方法
与队列相关
  • read_very_lazy
    只从 cookedq 读取已处理好的数据。
  • read_lazy
    如果 rawq 有数据,对 rawq 里的数据进行处理,然后从cookedq 中读取处理好的数据。 -
  • read_eager
    从系统的 socket buffer 接受数据(即非阻塞模式 1读取数据)并处理,然后从 cookedq 中读取数据。
  • read_very_eager
    与 read_eager 类似。不同之处在于 read_eager 只要从cookedq 成功读取到数据就返回,而 read_very_eager 会试图读尽可能多的数据。
其他read方法
  • read_until(expected, timeout=None)
    读取直到遇到给定的预期字节字符串,或者直到超时时间。

  • read_all( )
    读取所有数据,直到EOF以字节为单位;阻塞直到连接关闭。

其他方法
  • open(host, port=0[, timeout])
    连接到主机。可选的第二个参数是端口号,默认为标准Telnet端口(23)。可选的timeout参数以秒为单位指定用于阻止连接尝试之类的操作的超时(如果未指定,将使用全局默认超时设置)。
  • close( )
    关闭连接。
  • write(buffer)
    将字节字符串写入套接字
  • set_debuglevel( debuglevel )
  • 这个函数默认的参数时0,以上代码使用的是1,就将交互过程的信息都打印出来了。可以看到来回交互的都是byte stream。

Python netmiko模块

netmiko针对不同网络设备做了优化,可以更有效地处理SSH连接,它还支持不同的设备厂商和平台。

使用方法

在使用netmiko模块时,不可避免地会使用到ConnectHandler函数,ConnectHandler函数提供定义的字典来开始创建连接。
以下是ConnectHandler函数的定义及示例
定义

def ConnectHandler(*args, **kwargs):
    """Factory function selects the proper class and creates object based on device_type."""
    device_type = kwargs["device_type"]
    if device_type not in platforms:
        if device_type is None:
            msg_str = platforms_str
        else:
            msg_str = telnet_platforms_str if "telnet" in device_type else platforms_str
        raise ValueError(
            "Unsupported 'device_type' "
            "currently supported platforms are: {}".format(msg_str)
        )
    ConnectionClass = ssh_dispatcher(device_type)
    return ConnectionClass(*args, **kwargs)

示例

def netmiko_connect(request):
    """Connect to arista1 and return connection object"""
    password = os.getenv("PYNET_PASSWORD") if os.getenv("PYNET_PASSWORD") else getpass()
    arista1 = {
        "device_type": "arista_eos",
        "host": "arista1.lasthop.io",
        "username": "pyclass",
        "password": password,
    }

    net_connect = ConnectHandler(**arista1)

    def fin():
        net_connect.disconnect()

    request.addfinalizer(fin)
    return net_connect 
常用方法
常用
  • net_connect.enable() # 输入启用模式
  • net_connect.send_command() # 向下发送命令,返回输出(基于模式)
  • net_connect.send_config_set() # 将配置命令发送到远程设备
  • net_connect.disconnect() # 关闭连接
了解
  • net_connect.send_config_from_file() # 发送从文件加载的配置命令

  • net_connect.save_config() # 将running#config保存到startup#config

  • net_connect.send_command_timing() # 沿通道发送命令,返回输出(基于时序)

  • net_connect.find_prompt() # 返回当前路由器提示符

  • net_connect.commit() # 在Juniper和IOS#XR上执行提交操作

  • net_connect.write_channel() # 通道的低级写入

  • net_connect.read_channel() # 通道的低级写入

Python yaml模块

YAML是一种直观的能够被电脑识别的的数据序列化格式,是一个专门用来写配置文件的语言,容易被人类阅读,并且容易和脚本语言交互。

yaml文件规则
  1. 区分大小写;
  2. 使用缩进表示层级关系;
  3. 使用空格键缩进或者Tab键缩进,二者不能混用
  4. 缩进的空格数目不固定,只需要相同层级的元素左侧对齐
  5. 文件中的字符串不需要使用引号标注,但若字符串包含有特殊字符则需用引号标注
  6. 注释标识为#
yaml文件数据结构
  • 字典:键值对的集合,键值对用冒号 : 结构表示,冒号与值之间需用空格分隔
# yaml键值对嵌套:即python中字典嵌套字典
usr1:
  name: a
  psw: 123
usr2:
  name: b
  psw: 456

python解析该yaml文件后为

{'usr1': {'name': 'a', 'psw': 123}, 'usr2': {'name': 'b', 'psw': 456}}
  • 列表:一组按序排列的值,前加有 “-” 符号,符号与值之间需用空格分隔
# yaml键值对中嵌套数组
usr3:
  - a
  - b
  - c
usr4:
  - b

python解析该yaml文件后为

{'usr3': ['a', 'b', 'c'], 'usr4': ['b']}
  • 纯量(scalars):单个的、不可再分的值(如:字符串、bool值、整数、浮点数、时间、日期、null等),None值可用null可 ~ 表示
  • 多个文档在一个yaml文件,使用 — 分隔方式来分段
# 分段yaml文件中多个文档
---
animal1: dog
age: 2
---
animal2: cat
age: 3
常用方法
  • load() :读取yml文件,并返回一个对象
import yaml
f = open(r'E:\AutomaticTest\Test_Framework\config\config.yml')
y = yaml.load(f)
print (y)
  • load_all():生成一个迭代器
    如果string或文件包含几块yaml文档,你可以使用yaml.load_all来解析全部的文档。
import yaml
f = '''
---
name: James
age: 20
---
name: Lily
age: 19
'''
y = yaml.load_all(f)
for data in y:
    print(data)

输出结果为:

{'name': 'James', 'age': 20}
{'name': 'Lily', 'age': 19}
  • yaml.dump():将一个python对象生成为yaml文档
import yaml
aproject = {'name': 'Silenthand Olleander',
            'race': 'Human',
            'traits': ['ONE_HAND', 'ONE_EYE']
            }

print(yaml.dump(aproject,第二个为可选参数))

输出结果为:

name: Silenthand Olleander
race: Human
traits:
- ONE_HAND
- ONE_EYE

yaml.dump接收的第二个参数一定要是一个打开的文本文件或二进制文件,yaml.dump会把生成的yaml文档写到文件里。

  • yaml.dump_all():将多个段输出到一个文件中
import yaml

obj1 = {"name": "James", "age": 20}
obj2 = ["Lily", 19]

with open(r'E:\AutomaticTest\Test_Framework\config\config.yml', 'w') as f:
    yaml.dump_all([obj1, obj2], f)

两段数据参数用列表传入
输出结果为:

{age: 20, name: James}
--- [Lily, 19]
  • & 锚点和 * 别名,可以用来引用
defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults

test:
  database: myapp_test
  <<: *defaults

相当于:

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

test:
  database: myapp_test
  adapter:  postgres
  host:     localhost

& 用来建立锚点(defaults),<< 表示合并到当前数据,* 用来引用锚点。

Python jinja2模块

这个类的实例用于存储配置、全局对象,并用于从文件系统或其它位置加载模板。模板在Python中广泛使用,模板简单来说就是一个其中包涵占位变量表示动态的部分的文件,模板文件在经过动态赋值后,返回给用户(可以理解为渲染)。

jinja2的优点

jinja2之所以被广泛使用是因为它具有以下优点:

  • 相对于Template,jinja2更加灵活,它提供了控制结构,表达式和继承等。
  • 相对于Mako,jinja2仅有控制结构,不允许在模板中编写太多的业务逻辑。
  • 相对于Django模板,jinja2性能更好。
  • Jinja2模板的可读性很棒。
jinja2语法

作为一个模板系统,它还提供了特殊的语法,我们按照它支持的语法进行编写之后,就能使用jinja2模块进行渲染。

基本语法

在jinja2中,存在三种语法:

  • 控制结构 {% %}
  • 变量取值 { { }}
  • 注释 {# #}
    例子:
{# This is jinja code
 	filenames = {
    { filenames }}
    {% for file in filenames %}
    ...
    {% endfor %}
 
#}
jinja2中的过滤器

变量可以通过“过滤器”进行修改,过滤器可以理解为是jinja2里面的内置函数和字符串处理函数。
在这里插入图片描述
用法示例:

{
    { 'abc' | captialize  }}
# Abc
 
{
    { 'abc' | upper  }}
# ABC
 
{
    { 'hello world' | title  }}
# Hello World
 
{
    { "hello world" | replace('world','daxin') | upper }}
# HELLO DAXIN
 
{
    { 18.18 | round | int }}
# 18
常用方法

大多数应用都在初始化的时候撞见一个Environment对象,并用它加载模板。Environment支持两种加载方式

  • PackageLoader:包加载器
  • FileSystemLoader:文件系统加载器
    常用方法还包括:
  • get_template():获取模板目录下的某个具体文件,并会返回已加载的 Template。还可用来继承。
  • render():接受变量,对模板进行渲染

以PackageLoader为例:

from jinja2 import PackageLoader,Environment
file_loader = PackageLoader('templates') 我们定义一个PackageLoader。 # 从templates目录中检索模板。
env = Environment(loader=PackageLoader('python_project','templates'))    # 创建一个包加载器对象

template = env.get_template('bast.html')    # 使用get_template()方法获得模板
template.render(name='daxin',age=18)   # 渲染

PackageLoader()的两个参数为:python包的名称,以及模板目录名称。

FileSystemLoader:文件系统加载器,不需要模板文件存在某个Python包下,可以直接访问系统中的文件。

以FileSystemLoader为例的继承示例:

from jinja2 import Environment, FileSystemLoader

content = 'This is about page'

file_loader = FileSystemLoader('templates')
env = Environment(loader=file_loader)

template = env.get_template('about.html')

output = template.render(content=content)
print(output)

Python subprocess模块

subprocess使用时,父进程创建子进程去执行一个外部程序,并提供了标准输入输出和管道(pipe)的实现方法,同时获取它们的返回码。

使用方法
subprocess.popen()

subprocess.Popen类用于在一个新进程中执行一个子程序,程序运行subprocess.Popen()类,父进程创建子进程后,不会等待子进程执行完成。如果需要等待子进程,需要加入wait()方法阻塞父进程。

语法及参数

subprocess.Popen(args[, bufsize, stdin, stdout, stderr, …]):Popen类的构造函数,返回结果为subprocess.Popen对象。

  • args:需要执行的系统命令,可为字符串序列(列表或元组,shell为默认值False即可,建议为列表),也可为字符串(使用字符串时,需将shell赋值为True)
  • shell:默认为False,若args为序列时,shell=False;若args为字符串时,shell=True,表示通过shell执行命令
  • stdout、stdin、stderr:分别表示子程序标准输出、标准输入、标准错误,可为subprocess.PIPE、一个有效的文件描述符、文件对象或None。
    若为subprocess.PIPE:代表打开通向标准流的管道,创建一个新的管道;若为None表示没有任何重定向,子进程会继承父进程;stderr也可为subprocess.STDOUT,表示将子程序的标准错误输出重定向到了标准输出。
  • cwd:默认值为None;若非None,则表示将会在执行这个子进程之前改变当前工作目录;
  • bufsize:指定缓冲策略,0表示不缓冲,1表示行缓冲,其它整数表示缓冲区大小,负数表示使用系统默认值0;
  • env:用于指定子进程的环境变量。若env为None,那么子进程的环境变量将从父进程中继承;若env非None,则表示子程序的环境变量由env值来设置,它的值必须是一个映射对象。
  • universal_newlines: 不同系统的换行符不同。若True,则该文件对象的stdin,stdout和stderr将会以文本流方式打开;否则以二进制流方式打开。
subprocess.Popen对象常用方法

如:PopenObject为subprocess.Popen( )对象

  • PopenObject.communicate([input, timeout]):与进程进行交互(如发送数据到stdin、读取stdout和stderr数据),它会阻塞父进程,直到子进程完成。
    input:表示将发送到子进程的字符串数据,默认为None;
    timeout:超时判断,若超过timeout秒后仍未结束则抛出TimeoutExpired异常;
    communicate返回值:一个元组(stdout_data, stderr_data)

  • PopenObject.poll() :用于检查命令是否已经执行结束,若结束返回状态码;若未结束返回None

  • PopenObject.wait([timeout, endtime]):等待子进程结束,并返回状态码;若超过timeout(s)进程仍未结束,则抛出异常

  • PopenObject.send_signal(signal):发送信号signal给子进程

  • PopenObject.terminate():停止子进程

  • PopenObject.kill():杀死子进程

常用方法
  • subprocess.call(args[, stdout, …]):执行args命令,返回值为命令执行状态码(类似os.system);
    若未指定stdout,则命令执行后的结果输出到屏幕;
    若指定stdout,则命令执行后的结果输出到stdout;
    若执行成功,则函数返回值为0;若执行失败,则函数返回值为1;

  • subprocess.check_call(args[, stdout, …]):执行args命令,返回值为命令执行状态码;
    若未指定stdout,则命令执行后的结果输出到屏幕;
    若指定stdout,则命令执行后的结果输出到stdout;
    若执行成功,则函数返回值为0;若执行失败,抛出异常;

  • subprocess.check_output(args[, stderr, …]):执行args命令,返回值为命令执行的输出结果;
    若执行成功,则函数返回值为命令输出结果;若执行失败,则抛出异常;

以subprocess.call()为例,用法如下:

child = subprocess.call('python --version', shell =True)
print(child)
使用示例(好好研读帮助理解)
def subprocess_Popen1():
    print("***通过communicate函数分别输出PopenObject对象的输出流和错误流***")
    args = [["adb", "devices"], ["adb", "devices11"]]
    for arg in args:
        popen_object = subprocess.Popen(arg, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        object_stdout, object_stderr = popen_object.communicate()
        output = {"popen_object": popen_object,
                  "object_stdout": object_stdout,
                  "object_stderr": object_stderr}
        print(output)
    """
    {'popen_object': <subprocess.Popen object at 0x0000000002212400>, 'object_stdout': b'List of devices attached \r\n106D111805005938\tdevice\r\n\r\n', 'object_stderr': b''}
    {'popen_object': <subprocess.Popen object at 0x0000000002577C18>, 'object_stdout': b'', 'object_stderr': b'Android Debug Bridge version 1.0.31\r\n\r\n -a .....}
    """

    print("***通过stdout和stderr方法输出PopenObject对象输出流和错误流***")
    p0 = subprocess.Popen(["adb", "devices"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    object_stdout = p0.stdout.read()
    p0.stdout.close()
    object_stderr = p0.stderr.read()
    p0.stderr.close()
    print(object_stdout)        # 结果:b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n'
    print(object_stderr)        # 结果:b''

    print("***Popen对象stdin写入功能:使用stdout和stderr输出")
    args = ["python", "python1"]
    for arg in args:
        p4 = subprocess.Popen([arg], shell=True, stdout=subprocess.PIPE,
                              stdin=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
        p4.stdin.write("print('hello')")
        p4.stdin.close()
        out = p4.stdout.read()
        p4.stdout.close()
        err = p4.stderr.read()
        p4.stderr.close()
        print("out:%s err:%s" % (out, err))
    """
    ***Popen对象stdin写入功能
    out:hello
    err:
    out: err:'python1' 不是内部或外部命令,也不是可运行的程序或批处理文件。
    """

    print("***Popen对象stdin写入功能:使用communicate输出")
    p4 = subprocess.Popen(["python"], stdout=subprocess.PIPE,
                          stdin=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    p4.stdin.write("print('hello')")
    output = p4.communicate()
    print(output)       # 结果:('hello\n', '')

    print("***不含encoding参数***")
    p1 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE)
    out1 = p1.stdout.readlines()
    print(out1)         # 结果: [b'List of devices attached \r\n', b'106D111805005938\tdevice\r\n', b'\r\n']

    print("***含encoding参数***")
    p2 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE, encoding="utf-8")
    out2 = p2.stdout.readlines()
    print(out2)         # 结果: ['List of devices attached \n', '106D111805005938\tdevice\n', '\n']

    print("***Popen对象检查命令是否结束,等待进程结束")
    print(p2.poll())    # 结果: None
    print(p2.wait())    # 结果: 0
    print(p2.poll())    # 结果: 0

    print("***Popen对象communicate函数,它会阻塞父进程直至子进程完成")
    p3 = subprocess.Popen("adb devices", shell=True, stdout=subprocess.PIPE)
    out = p3.communicate()[0]
    print(out)          # 结果:b'List of devices attached \r\n338b123f0504\tdevice\r\n\r\n'
    print(p3.poll())    # 结果:0
subprocess_Popen1()



def subprocess_Popen2():
    """
    1. 通过管道功能,实现adb shell ps | findstr top功能
    2. 直接为args赋值为一个字符串,实现adb shell ps | findstr top功能
    :return:
    """
    print("***通过管道方式***")
    p1 = subprocess.Popen(["adb", "shell", "ps"], stdout=subprocess.PIPE)
    p2 = subprocess.Popen(["findstr", "top"], stdin=p1.stdout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = p2.communicate()
    print(out, err)         # 结果:b'shell     8508  8504  2600   1044  c004e5f8 b6f40938 S top\r\r\n' b''
    print("***通过传一个字符串方式***")
    p3 = subprocess.Popen("adb shell ps | findstr top", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    out, err = p3.communicate()
    print(out, err)         # 结果:b'shell     8508  8504  2600   1044  c004e5f8 b6f40938 S top\r\r\n' b''
subprocess_Popen2()

Python fabric模块

Fabric命令及相关参数总结
fab命令参数
  • -f:指定fab入口文件,默认为fabfile.py。
  • -l:显示可用任务函数名。
  • -H:指定目标主机,多台主机用“,”分隔
  • -R:指定角色(Role)。
  • -w:当命令执行失败,发出告警,而非默认终止任务。
  • -P:以异步并行方式运行多台主机任务,默认为串行运行。
  • -t:设置设备连接超时时间。
  • -T:设置远程主机命令执行超时时间。
  • -g:指定网关(中转设备),比如堡垒机环境,填写堡垒机IP即可。
方法总结
  • local:执行本地命令,如:local(‘hostname’)
  • lcd:切换本地目录,lcd(’/root’)
  • cd:切换远程目录,cd(‘cd’)
  • run:执行远程命令,如:run(‘hostname R1’)
  • sudo:sudo执行远程命令,如:sudo('echo “123456″)
  • put:上传本地文件到远程主机,如:put(src,des)
  • get:从远程主机下载文件到本地,如:get(des,src)
  • prompt:获取用户输入信息,如:prompt(‘please enter a new password:’)
  • reboot:重启远程主机,reboot()
  • @task:函数修饰符,标识的函数为fab可调用的
  • @runs_once:函数修饰符,表示的函数只会执行一次
Fabric简单脚本
简单举例

我们创建一个”fabfile.py”文件,”fabfile.py”文件中每个函数就是一个任务,任务名即函数名,例中是”hello”。”fab”命令就是用来执行”fabfile.py”中定义的任务,它必须显式地指定任务名。你可以使用参数”-l”来列出当前”fabfile.py”文件中定义了哪些任务:

def hello():
    print "Hello Fabric!"

在”fabfile.py”的目录下执行命令:

$ fab hello

任务可以带参数,比如我们将hello函数改为:

def hello(name, value):
    print "Hello Fabric! %s=%s" % (name,value)

此时执行hello任务时,就要传入参数值:

$ fab hello:name=Year,value=2016
本地fabric脚本

“fabric.api”包里的local()方法可以用来执行本地Shell命令,比如让我们列出本地”/home/ckr”目录下的所有文件及目录:

from fabric.api import local

def hello():
    local('ls -l /home/ckr/')

local()方法有一个capture参数用来捕获标准输出,比如:

def hello():
    output = local('echo Hello', capture=True)

Fabric默认执行的脚本是fabfile.py,如果要换脚本文件需要使用 -f 指定。比如我们将hello任务放到.py中就要执行:

fab -f .py hello
远程fabric脚本(重,主)

Fabric真正强大之处不是在执行本地命令,而是可以方便的执行远程机器上的Shell命令。它通过SSH实现,你需要的是在脚本中配置远程机器地址及登录信息:

SSH自动登陆

将登陆密码写到脚本文件里是不安全的,推荐的方法是设置SSH KEY自动登陆。

登陆本地机器生成KEY:

$ ssh-keygen -t rsa -f ~/.ssh/id_rsa_fabric

生成密钥对之后将公钥添加到远程服务器的~/.ssh/authorized_keys文件中,就可以实现自动登陆了。

#-*- coding:utf-8 -*-
from fabric.api import env, roles, run, execute, cd

env.hosts = ['10.216.224.65', '10.216.224.66']
env.user = 'user'
env.key_filename = '~/.ssh/id_rsa_fabric'

def taskA():
    with cd('/usr/local/webserver'):
        run('pwd')

※注意:authorized_keys文件权限只所有者可写,其他用户均无写权限,否则sshd将认为不安全不允许使用该文件导致还需要输入密码认证。

执行远程命令
常用环境变量(env.)
  • user:可以通过设置env.user来指定Fabric建立SSH连接时使用的用户名(默认使用本地用户名)。
  • password:用来显式设置默认连接或者在需要的时候提供sudo密码。如果没有设置密码或密码错误,Fabric将会提示你输入。
  • passwords:密码字典,针对不同的机器设置密码。必须由username@host:port三部分构成,缺一不可,否则运行时还是会要求输入密码。
  • warn_only:布尔值,用来设置Fabric是否在检测到远程错误时退出。
  • hosts:全局主机列表。
  • roledefs:定义角色名和主机列表的映射字典。
  • getway:定义网关
  • port:定义目标主机端口,默认为22
#-*- coding:utf-8 -*-
from fabric.api import run, env

# env被称为环境字典,用来配置一些运行环境相关的信息
env.hosts = ['192.168.1.100', '192.168.1.101']
env.user = 'user'
env.password = 'passwd'

def taskA():
    run('cd /usr/local/webserver/php && ls -l')
    run('sudo /usr/local/webserver/nginx/sbin/nginx -t')

fabric.api包里的run()方法可以用来执行远程Shell命令。上面的任务会分别到两台服务器”192.168.1.100”和”192.168.1.101”上执行命令。这里假设两台服务器的用户名都是”user”,密码都是“passwd”。

env.hosts是设置机器列表的,也可以把用户直接写到hosts里:

env.hosts = ['[email protected]', '[email protected]']

如果你的env.hosts里没有配置某个服务器,但是你又想在这个服务器上执行任务,你可以在命令行中通过-H指定远程服务器地址,多个服务器地址用逗号分隔:

fab -H 192.168.1.102,192.168.1.103 taskA

如果对于不同的服务器想执行不同的任务,上面的程序就做不到了,我们需要对服务器定义角色:

#-*- coding:utf-8 -*-
from fabric.api import env, roles, run, execute, cd

env.roledefs = {
    'dev': ['[email protected]', '[email protected]'],
    'online': ['[email protected]']
}

# host strings必须由username@host:port三部分构成,缺一不可,否则运行时还是会要求输入密码
env.passwords = {
    '[email protected]:22': 'passwd1',
    '[email protected]:22': 'passwd2',
    '[email protected]:22': 'passwd3'
}

@roles('dev')
def taskA():
    with cd('/usr/local/webserver'):
        run('pwd')

@roles('online')
def taskB():
    run('pwd')

def task():
    execute(taskA)
    execute(taskB)

然后执行task任务:

$ fab task

Fabric会在dev机器上执行taskA任务,然后在online机器上执行taskB任务。@roles装饰器指定了它所装饰的任务会被哪个角色的服务器执行。

如果某一任务上没有指定某个角色,但是你又想让这个角色的服务器也能运行该任务,你可以通过-R来指定角色名,多个角色用逗号分隔:

$ fab -R online taskA
SSH功能函数

到目前为止,我们介绍了local()和run()函数分别用来执行本地和远程Shell命令。Fabric还提供了其他丰富的功能函数来辅助执行命令,这里我们介绍几个常用的:

  • sudo: 以超级用户权限执行远程命令

功能类似于run()方法,区别是它相当于在Shell命令前加上了sudo,所以拥有超级用户的权限。使用此功能前,你需要将你的用户设为sudoer,而且无需输密码。

from fabric.api import env, sudo

env.hosts = ['[email protected]', '[email protected]']
env.password = '111111'

def hello():
    sudo('mkdir /var/www/myapp')
  • get(remote, local): 从远程机器上下载文件到本地

它的工作原理是基于scp命令,使用的方法如下:

from fabric.api import env, get

env.hosts = ['[email protected]',]
env.password = '111111'

def hello():
    get('/var/log/myapp.log', 'myapp-0301.log')

上述任务将远程机上”/var/log/myapp.log”文件下载到本地当前目录,并命名为”myapp-0301.log”。

  • put(local, remote): 从本地上传文件到远程机器上

同get一样,put方法也是基于scp命令,使用的方法如下:

from fabric.api import env, put

env.hosts = ['[email protected]', '[email protected]']
env.password = '111111'

def hello():
    put('/tmp/myapp-0301.tar.gz', '/var/www/myapp.tar.gz')

上述任务将本地”/tmp/myapp-0301.tar.gz”文件分别上传到两台远程机的”/var/www/“目录下,并命名为”myapp.tar.gz”。如果远程机上的目录需要超级用户权限才能放文件,可以在put()方法里加上use_sudo参数:

put('/tmp/myapp-0301.tar.gz', '/var/www/myapp.tar.gz', use_sudo=True)
  • prompt(): 提示输入

该方法类似于Shell中的read命令,它会在终端显示一段文字来提示用户输入,并将用户的输入保存在变量里:

from fabric.api import env, get, prompt

env.hosts = ['[email protected]',]
env.password = '111111'

def hello():
    filename = prompt('Please input file name: ')
    get('/var/log/myapp.log', '%s.log' % filename)

现在下载后的文件名将由用户的输入来决定。我们还可以对用户输入给出默认值及类型检查:

port = prompt('Please input port number: ', default=8080, validate=int)

执行任务后,终端会显示:

Please input port number: [8080] 

如果你直接按回车,则”port”变量即为默认值”8080”;如果你输入字符串,终端会提醒你类型验证失败,让你重新输入,直到正确为止。

  • reboot: 重启服务器

看方法名就猜到了,有时候安装好环境后,需要重启服务器,这时就要用到reboot()方法,你可以用wait参数来控制其等待多少秒后重启,没有此参数则代表立即重启:

from fabric.api import env, reboot

env.hosts = ['[email protected]',]
env.password = '111111'

def restart():
    reboot(wait=60)

上面的restart任务将在一分钟后重启服务器。

Fabric上下文管理器

Fabric的上下文管理器是一系列与Python的”with”语句配合使用的方法,它可以在”with”语句块内设置当前工作环境的上下文。让我们介绍几个常用的:

  • cd(): 设置远程机器的当前工作目录

cd()方法在之前的范例中出现过,with cd()语句块可以用来设置远程机的工作目录:

from fabric.api import env, cd, put

env.hosts = ['[email protected]', ]
env.password = '111111'

def hello():
    with cd('/var/www/'):
        put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz')

上例中的文件会上传到远程机的”/var/www/“目录下。出了with cd()语句块后,工作目录就回到初始的状态,也就是”ckr”用户的根目录。

  • lcd(): 设置本地工作目录

lcd()就是”local cd”的意思,用法同cd()一样,区别是它设置的是本地的工作目录:

from fabric.api import env, cd, lcd, put

env.hosts = ['[email protected]', ]
env.password = '111111'

def hello():
    with cd('/var/www/'):
        with lcd('/tmp/'):
            put('myapp-0301.tar.gz', 'myapp.tar.gz')

这个例子的执行效果跟上个例子一样。

  • path: 添加远程机的PATH路径
from fabric.api import env, run, path

env.hosts = ['[email protected]', ]
env.password = '111111'

def hello():
    with path('/home/ckr/tmp'):
        run('echo $PATH')
    run('echo $PATH')

假设我们的PATH环境变量默认是”/sbin:/bin”,在上述with path()语句块内PATH变量将变为”/sbin:/bin:/home/ckr/tmp”。出了with语句块后,PATH又回到原来的值

  • settings(): 设置Fabric环境变量参数

Fabric环境变量即是我们例子中一直出现的fabric.api.env,它支持的参数可以从官方文档中查到。

from fabric.api import env, run, settings

env.hosts = ['[email protected]', ]
env.password = '111111'

def hello():
    with settings(warn_only=True):
        run('echo $USER')

我们将环境参数warn_only暂时设为True,这样遇到错误时任务不会退出。

  • shell_env(): 设置Shell环境变量

可以用来临时设置远程和本地机上Shell的环境变量。

from fabric.api import env, run, local, shell_env

env.hosts = ['[email protected]', ]
env.password = '111111'

def hello():
    with shell_env(JAVA_HOME='/opt/java'):
        run('echo $JAVA_HOME')
        local('echo $JAVA_HOME')
  • prefix: 设置命令执行前缀
from fabric.api import env, run, local, prefix

env.hosts = ['[email protected]', ]
env.password = '111111'

def hello():
    with prefix('echo Hi'):
        run('pwd')
        local('pwd')

在上述with prefix()语句块内,所有的run()或local()方法的执行都会加上echo Hi &&前缀,也就是效果等同于:

        run('echo Hi && pwd')
        local('echo Hi && pwd')

配合后一节我们会讲到的错误处理,它可以确保在prefix()方法上的命令执行成功后才会执行语句块内的命令。

错误处理

默认情况下,Fabric在任务遇到错误时就会退出,如果我们希望捕获这个错误而不是退出任务的话,就要开启warn_only参数。在上面介绍settings()上下文管理器时,我们已经看到了临时开启warn_only的方法了,如果要全局开启,有两个办法:

  1. 在执行fab命令时加上-w参数
$ fab -w hello
  1. 设置env.warn_only环境参数为True
from fabric.api import env

env.warn_only = True

现在遇到错误时,控制台会打出一个警告信息,然后继续执行后续任务。那我们怎么捕获错误并处理呢?像run(), local(), sudo(), get(), put()等SSH功能函数都有返回值。当返回值的succeeded属性为True时,说明执行成功,反之就是失败。你也可以检查返回值的failed属性,为True时就表示执行失败,有错误发生。在开启warn_only后,你可以通过failed属性检查捕获错误,并执行相应的操作。示例如下:

from fabric.api import env, cd, put

env.hosts = ['[email protected]', ]
env.password = '111111'

def hello():
    with cd('/var/www/'):
        upload = put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz')
        if upload.failed:
            sudo('rm myapp.tar.gz')
            put('/tmp/myapp-0301.tar.gz', 'myapp.tar.gz', use_sudo=True)
并行执行

我们在介绍执行远程命令时曾提到过多台机器的任务默认情况下是串行执行的。Fabric支持并行任务,当服务器的任务之间没有依赖时,并行可以有效的加快执行速度。怎么开启并行执行呢?办法也是两个:

  1. 在执行fab命令时加上-P参数
$ fab -P hello
  1. 设置env.parallel环境参数为True
from fabric.api import env

env.parallel = True

以上是对任务并行做一个全局控制。如果只想对某一个任务做并行的话,我们可以在任务函数上加上@parallel装饰器,这样即便全局并行未开启,被@parallel装饰的任务也会并行执行:

from fabric.api import parallel

@parallel
def runs_in_parallel():
    pass

def runs_serially():
    pass

这样即便并行未开启,runs_in_parallel()任务也会并行执行。

反过来,我们可以在任务函数上加上@serial装饰器:

from fabric.api import serial

def runs_in_parallel():
    pass

@serial
def runs_serially():
    pass

这样即便并行已经开启,runs_serially()任务也会串行执行。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/w666666Wwwwwww/article/details/136455420

智能推荐

初始Linux的基本操作_桌面操作系统,手机操作系统,服务器操作系统,嵌入式操作系统-程序员宅基地

文章浏览阅读652次。Linux的相关概念和理解Linux的基本指令ls,cd,pwd, touch, mkdir, whoami, tree, adduser, userdel实现_桌面操作系统,手机操作系统,服务器操作系统,嵌入式操作系统

centos8使用命令修改主机名_centos8stream 关于的名称-程序员宅基地

文章浏览阅读1.1k次。hostnamectl set-hostname layuiFilehostname #查看主机名然后先跳到普通用户再exit退到root看其生效或reboot重启生效_centos8stream 关于的名称

安卓高手之路之图形系统(6)requestLayout的流程_kotlin requestlayout-程序员宅基地

文章浏览阅读1.1w次。当一个View调用requestLayout的时候,会给当前的View设置一个FORCE_LAYOUT标记。由此向ViewParent请求布局。这样从这个View开始向上一直requestLayout。最终到达ViewRootImpl。ViewParent 就是当前的传输链。【参见职责链设计模式】 第一步。ViewRootImpl发现请求了布局。那么就会调用measure方_kotlin requestlayout

python opencv 灰度图转换和resize函数的使用_python cv2 resize(img1, ())-程序员宅基地

文章浏览阅读1.7k次。import cv2import numpy as npimg1 = cv2.imread('left.png',1)img1 = cv2.resize(img1, (797,1212))dst1 = cv2.cvtColor(img1,cv2.COLOR_BGR2GRAY)cv2.imwrite('l.png',dst1)img1 = cv2.imread('left.png',1..._python cv2 resize(img1, ())

Socket基础六:基于数据报套接字的网络程序(udp丢包测试)_数据报套接字 丢包测试-程序员宅基地

文章浏览阅读656次。Socket基础五:基于数据报套接字的网络程序(udp丢包测试)作者:刘磊 2020.4.27   参考书目:《Windows网络编程》刘琰等著一、功能函数1、int udp_server_fun_packetloss(SOCKET s){ int iResult = 0; int count = 0; struct sockaddr_in cliaddr; int addrlen = sizeof(sockaddr_in); char recvline[MAXLINE]; do_数据报套接字 丢包测试

国内elasticsearch和kibana镜像、ik分词器_ik分词器国内镜像-程序员宅基地

文章浏览阅读1.6k次。发现华为云有一个好用的国内镜像kibana各个版本下载地址:https://mirrors.huaweicloud.com/elasticsearch/kibana各个版本下载地址:https://mirrors.huaweicloud.com/kibana/ik分词器各个版本下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases亲测下载速度非常快..._ik分词器国内镜像

随便推点

Coursera—Andrew Ng机器学习—课程笔记 Lecture 5 Octave Tutorial-程序员宅基地

文章浏览阅读575次。定义函数 squareThisNumber(x),内容如下:

超级应用 - 免费应用内测托管平台|APP应用分发平台|iOS应用分发|Android应用分发|免费应用内测托管平台 源码下载_awjd1.tv-程序员宅基地

文章浏览阅读4k次。网站标题:超级应用 - 免费应用内测托管平台|APP应用分发平台|iOS应用分发|Android应用分发|免费应用内测托管平台源码下载网站关键词:超级应用(www.awwjd.com) 为您提供测试测试应用、应用托管、分发测试、兼容测试等,为客户提供APP托管和免费的下载分发渠道!源码下载网站描述:超级应用,分发平台,内测平台,内测分发,app分发,ios企业签名,ios分发平台,ios免签封装,苹果企业签名,苹果超级签名,免签封装,应用打包,苹果App下载,安卓应用,苹果应用,扫码下载,UDID,iO_awjd1.tv

阿里数据库内核月报:2017年05月-程序员宅基地

文章浏览阅读45次。# 01MySQL·引擎特性·InnoDBBufferPool# 02AliSQL·特性介绍·动态加字段# 03PgSQL·特性分析·数据库崩溃恢复(上)# 04MySQL·答疑解惑·MySQL的那些网络超时错误# 05HybridDB·最佳实践·HybridDB数据合并的方法与原理...

FPGA时序约束、时序分析(一)-程序员宅基地

文章浏览阅读163次。很多人询问关于约束、时序分析的问题,比如:如何设置setup,hold时间?如何使用全局时钟和第二全局时钟(长线资源)?如何进行分组约束?如何约束某部分组合逻辑?如何通过约束保证异步时钟域之间的数据交换可靠?如何使用I/O逻辑单元内部的寄存器资源?如何进行物理区域约束,完成物理综合和物理实现?为了解决大家的疑难,我们将逐一讨论这些问题。(注:以下主要设计时序约束)A 时序约束的概念和基本策略..._如何约束fpga寄存器到pad的延时

2024年软件测试工具总结 —— 性能测试工具_magicotp,软件测试基础入门_web页面性能测试工具-程序员宅基地

文章浏览阅读512次,点赞19次,收藏12次。WebLOAD是一款针对Web应用程序的企业级负载和性能测试工具,提供性能、完整性和可伸缩性测试等功能,能够同时模拟数千个用户,因此您可以测试重流量负载,并报告应用程序中的弱点、约束和性能瓶颈。它是一个以开发人员为中心(当然,测试人员亦可以使用,因为真的很方便),免费和开源的负载测试工具,旨在使性能测试具有生产力和令人愉悦的体验,可最大程度地减少系统资源的消耗。在线性能监控是指借助监控工具,监控系统性能的实际数据,因为是真实数据,比研发环境中通过工具产生负载得到的测试结果更客观,更有分析价值。_web页面性能测试工具

Java进制转换, 数据类型, 运算符_java 32进制递增-程序员宅基地

文章浏览阅读511次。 1:进制转换转换规则:先把数据的每一位上的系数乘以对应基数的次幂(低位从零开始),然后相加即可十进制到其他进制规则:除基取余,直到商为0,最后将余数反转十进制到二进制: 除2取余,直到商为0,最后将余数反转 例: 十进制13对应的二进制数据是1101 二进制到十进制的技巧(8421码)十进制到八进制: 除8取余,直到商为0,最后将余数反转 例: ..._java 32进制递增