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

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan