第 7 章 跨设备编程
本章攻略:
- 使用telnet在远程主机中执行shell命令
- 通过SFTP把文件复制到远程设备中
- 打印远程设备的CPU信息
- 在远程主机中安装Python包
- 在远程主机中运行MySQL命令
- 通过SSH把文件传输到远程设备中
- 远程配置Apache运行网站
7.1 简介
本章推荐一些有趣的Python库。这些攻略的目的是,向系统管理员和高级Python程序员介绍如何编写代码连接远程系统执行命令。本章首先介绍使用Python内置库telnetlib
编写的简单攻略,然后介绍知名的远程连接库paramiko
,最后介绍强大的远程系统管理库fabric
。经常编写脚本完成自动化部署任务(例如部署Web应用或编译应用的二进制文件)的开发者很喜欢fabric
库。
7.2 使用telnet在远程主机中执行shell命令
如果想通过telnet连接旧的网络交换机或路由器,无需使用bash脚本或交互式shell,可以在Python脚本中完成这一操作。这个攻略要创建一个简单的telnet会话,说明如何在远程主机中执行shell命令。
7.2.1 准备工作
你需要在自己的设备中安装telnet服务器,并确保其能正常运行。你可以使用操作系统专用的包管理器安装telnet服务器包。例如,在Debian/Ubuntu中,可以使用apt-get
或aptitude
安装telnetd
包,如下面的命令所示:
$ sudo apt-get install telnetd
$ telnet localhost
7.2.2 实战演练
我们来定义一个函数,从命令行中获取用户登录凭据,然后连接telnet服务器。
成功连接后,这个函数会把ls
命令发送给服务器,然后显示命令的输出,例如列出一个目录中的内容。
代码清单7-1是一个telnet会话的代码,在远程主机中执行一个Unix命令,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 7
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import getpass
import sys
import telnetlib
HOST = "localhost"
def run_telnet_session():
user = raw_input("Enter your remote account: ")
password = getpass.getpass()
session = telnetlib.Telnet(HOST)
session.read_until("login: ")
session.write(user + "\n")
if password:
session.read_until("Password: ")
session.write(password + "\n")
session.write("ls\n")
session.write("exit\n")
print session.read_all()
if __name__ == '__main__':
run_telnet_session()
如果本地设备中运行有telnet服务器,运行这个脚本后,会要求你输入远程主机的用户账户和密码。在Debian设备中执行这个telnet会话得到的输出如下所示:
$ python 7_1_execute_remote_telnet_cmd.py
Enter remote hostname e.g. localhost: localhost
Enter your remote account: faruq
Password:
ls
exit
Last login: Mon Aug 12 10:37:10 BST 2013 from localhost on pts/9
Linux debian6 2.6.32-5-686 #1 SMP Mon Feb 25 01:04:36 UTC 2013 i686
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in usrshare/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have new mail.
faruq@debian6:~$ ls
down Pictures Videos
Downloads projects yEd
Dropbox Public
env readme.txt
faruq@debian6:~$ exit
logout
7.2.3 原理分析
这个攻略使用Python内置的telnetlib
网络库创建telnet会话。run_telnet_session()
函数从命令行中获取用户名和密码。获取密码使用的是getpass
模块中的getpass()
函数,这个函数不会让你看到屏幕中输入的内容。
为了创建telnet会话,需要实例化Telnet
类,初始化时要指定主机名参数。在这个攻略中,主机名是localhost
。你可以使用argparse
模块把主机名传给脚本。
telnet会话的远程输出可以使用read_until()
方法获取。登录提示符就是使用这个方法检测到的。然后,使用write()
方法把用户名和一个换行符发送给远程设备(在这个攻略中,把同一台设备当做远程主机)。再使用类似的方式把密码提供给远程主机。
然后,把ls
密令发送给远程设备执行。最后,发送exit
命令中断连接,再使用read_all()
方法获取从远程主机中接收的全部会话数据,将其打印在屏幕上。
7.3 通过SFTP把文件复制到远程设备中
如果想安全地把本地设备中的文件上传或复制到远程设备中,可以使用“安全文件传输协议”(Secure File Transfer Protocol,简称SFTP)。
7.3.1 准备工作
这个攻略要使用一个强大的第三方网络库paramiko
,演示如何使用SFTP复制文件,如下面的命令所示。paramiko
的最新代码可以从GitHub(https://github.com/paramiko/paramiko)上获取,或者使用PyPI安装:
$ pip install paramiko
7.3.2 实战演练
这个攻略要从命令行中接收一些输入值,包括远程主机名、服务器端口、源文件名、目标文件名。简单起见,我们可以使用默认值或者硬编码的值。
连接远程服务器需要用户名和密码,这两个值可以从用户在命令行中的输入获取。
代码清单7-2说明了如何通过SFTP把文件复制到远程主机中,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 7
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import paramiko
import getpass
SOURCE = '7_2_copy_remote_file_over_sftp.py'
DESTINATION ='tmp7_2_copy_remote_file_over_sftp.py '
def copy_file(hostname, port, username, password, src, dst):
client = paramiko.SSHClient()
client.load_system_host_keys()
print " Connecting to %s \n with username=%s... \n" %(hostname,username)
t = paramiko.Transport((hostname, port))
t.connect(username=username,password=password)
sftp = paramiko.SFTPClient.from_transport(t)
print "Copying file: %s to path: %s" %(src, dst)
sftp.put(src, dst)
sftp.close()
t.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Remote file copy')
parser.add_argument('--host', action="store", dest="host", default='localhost')
parser.add_argument('--port', action="store", dest="port", default=22, type=int)
parser.add_argument('--src', action="store", dest="src", default=SOURCE)
parser.add_argument('--dst', action="store", dest="dst", default=DESTINATION)
given_args = parser.parse_args()
hostname, port = given_args.host, given_args.port
src, dst = given_args.src, given_args.dst
username = raw_input("Enter the username:")
password = getpass.getpass("Enter password for %s: " %username)
copy_file(hostname, port, username, password, src, dst)
运行这个脚本后,会看到类似下面的输出:
$ python 7_2_copy_remote_file_over_sftp.py
Enter the username:faruq
Enter password for faruq:
Connecting to localhost
with username=faruq...
Copying file: 7_2_copy_remote_file_over_sftp.py to path:
tmp7_2_copy_remote_file_over_sftp.py
7.3.3 原理分析
这个攻略可以接收不同的输入值,然后连接到远程设备,通过SFTP复制文件。
这个攻略把命令行中的输入传给copy_file()
函数,然后使用paramiko
库中的SSHClient
类创建一个SSH客户端。这个客户端需要加载系统的主机密钥。然后创建一个Transport
类的实例,连接远程服务器。真正的SFTP连接对象sftp
由paramiko
库中的SFTPClient.from_transport()
函数创建,其参数是Transport
类的实例。
SFTP连接好之后,使用put()
方法借由这个连接把本地文件复制到远程主机中。
最后,分别在各个对象上调用close()
方法,清理SFTP连接和底层对象。这是一个好习惯。
7.4 打印远程设备的CPU信息
有时,我们需要通过SSH在远程设备中运行一个简单的命令。例如,查询远程设备的CPU或RAM信息。这种操作可以使用本节中的Python脚本完成。
7.4.1 准备工作
你需要安装第三方库paramiko
,如下面的命令所示。paramiko
的源代码可从GitHub仓库中获取,地址为https://github.com/paramiko/paramiko。
$ pip install paramiko
7.4.2 实战演练
我们可以使用paramiko
模块创建一个远程会话连接Unix设备。
然后,通过这个会话,我们可以读取远程设备中的proccpuinfo文件,获取CPU信息。
代码清单7-3是打印远程设备CPU信息的代码,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 7
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
import argparse
import getpass
import paramiko
RECV_BYTES = 4096
COMMAND = 'cat proccpuinfo'
def print_remote_cpu_info(hostname, port, username, password):
client = paramiko.Transport((hostname, port))
client.connect(username=username, password=password)
stdout_data = []
stderr_data = []
session = client.open_channel(kind='session')
session.exec_command(COMMAND)
while True:
if session.recv_ready():
stdout_data.append(session.recv(RECV_BYTES))
if session.recv_stderr_ready():
stderr_data.append(session.recv_stderr(RECV_BYTES))
if session.exit_status_ready():
break
print 'exit status: ', session.recv_exit_status()
print ''.join(stdout_data)
print ''.join(stderr_data)
session.close()
client.close()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Remote file copy')
parser.add_argument('--host', action="store", dest="host", default='localhost')
parser.add_argument('--port', action="store", dest="port", default=22, type=int)
given_args = parser.parse_args()
hostname, port = given_args.host, given_args.port
username = raw_input("Enter the username:")
password = getpass.getpass("Enter password for %s: " %username)
print_remote_cpu_info(hostname, port, username, password)
运行这个脚本后会显示指定主机的CPU信息,这里连接的是本地设备,如下所示:
$ python 7_3_print_remote_cpu_info.py
Enter the username:faruq
Enter password for faruq:
exit status: 0
processor: 0
vendor_id: GenuineIntel
cpu family: 6
model: 42
model name: Intel(R) Core(TM) i5-2400S CPU @ 2.50GHz
stepping: 7
cpu MHz: 2469.677
cache size: 6144 KB
fdiv_bug: no
hlt_bug: no
f00f_bug: no
coma_bug: no
fpu: yes
fpu_exception: yes
cpuid level: 5
wp: yes
flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_
tsc up pni monitor ssse3 lahf_lm
bogomips: 4939.35
clflush size: 64
cache_alignment: 64
address sizes: 36 bits physical, 48 bits virtual
power management:
7.4.3 原理分析
首先,收集连接所需的参数,例如主机名、端口号、用户名和密码。然后,把这些参数传给print_remote_cpu_info()
函数。
在这个函数中,使用paramiko
模块中的Transport
类创建了一个SSH客户端会话。然后使用提供的用户名和密码连接远程设备。我们可以在SSH客户端上调用open_channel()
方法创建一个原始通信会话。若想在远程主机中执行命令,可以使用exec_command()
方法。
把命令发送给远程主机之后,可以通过封阻会话对象的recv_ready()
事件来获取远程主机的响应。我们可以创建两个列表,stdout_data
和stderr_data
,用来存储远程主机的输出和错误消息。
命令在远程设备中退出时,可以使用exit_status_ready()
方法检测到。接收到的远程会话数据使用join()
方法串接起来。
最后,分别在各对象上调用close()
方法,中断会话和客户端连接。
7.5 在远程主机中安装Python包
在前面几个攻略中,你可能注意到了,在远程主机中执行操作时,要写很多代码建立连接。为了提高执行效率,最好抽象这些代码,只向程序员开放相对高级的操作。在远程设备中执行命令时总是要建立连接,这个过程很繁琐,也浪费时间。
Fabric(http://fabfile.org/)这个Python第三方模块可以解决这个问题。它只开放了适当数量的API,能高效地和远程设备交互。
这个攻略举个简单的例子说明如何使用Fabric。
7.5.1 准备工作
首先,我们要安装Fabric。你可以使用Python包管理工具pip
或easy_install
安装,如下面的命令所示。Fabric依赖于paramiko
模块,安装Fabric时会自动安装paramiko
。
$ pip install fabric
这里,我们要通过SSH协议连接远程主机,所以必须在远程主机中运行SSH服务器。如果想在本机中测试(假装是连接到远程设备),可以在本地安装openssh
服务器包。在Debian/Ubuntu中,可以使用包管理器apt-get
安装,如下面的命令所示:
$ sudo apt-get install openssh-server
7.5.2 实战演练
下面这段代码说明了如何使用Fabric安装Python包。
代码清单7-4给出了在远程主机中安装Python包所需的代码,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 7
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
from getpass import getpass
from fabric.api import settings, run, env, prompt
def remote_server():
env.hosts = ['127.0.0.1']
env.user = prompt('Enter user name: ')
env.password = getpass('Enter password: ')
def install_package():
run("pip install yolk")
Fabric脚本和普通Python脚本的运行方式不一样。使用fabric
库定义的所有函数都要保存在一个名为fabfile.py的Python脚本中。在这个脚本中,没有传统的__main__
指令。你可以使用Fabric API定义方法,然后使用命令行工具fab
执行。因此,我们不使用python
运行Fabric脚本,而是在当前目录中创建一个fabfile.py脚本,然后执行fab one_function_name another_function_name
。
那么,我们来按照下面命令中的方式创建fabfile.py脚本。为了简化操作,你可以创建文件快捷方式,或者把其他文件链接到fabfile.py脚本上。首先,删除前面创建的fabfile.py文件,然后创建一个指向fabfile的快捷方式:
$ rm -rf fabfile.py
$ ln -s 7_4_install_python_package_remotely.py fabfile.py
如果运行fabfile,在远程主机中安装Python包yolk
后会生成如下输出:
$ ln -sfn 7_4_install_python_package_remotely.py fabfile.py
$ fab remote_server install_package
Enter user name: faruq
Enter password:
[127.0.0.1] Executing task 'install_package'
[127.0.0.1] run: pip install yolk
[127.0.0.1] out: Downloading/unpacking yolk
[127.0.0.1] out: Downloading yolk-0.4.3.tar.gz (86kB):
[127.0.0.1] out: Downloading yolk-0.4.3.tar.gz (86kB): 100% 86kB
[127.0.0.1] out: Downloading yolk-0.4.3.tar.gz (86kB):
[127.0.0.1] out: Downloading yolk-0.4.3.tar.gz (86kB): 86kB
downloaded
[127.0.0.1] out: Running setup.py egg_info for package yolk
[127.0.0.1] out: Installing yolk script to homefaruq/env/bin
[127.0.0.1] out: Successfully installed yolk
[127.0.0.1] out: Cleaning up...
[127.0.0.1] out:
Done.
Disconnecting from 127.0.0.1... done.
7.5.3 原理分析
这个攻略演示了如何使用Python脚本在远程主机中执行系统管理任务。这个脚本中定义了两个函数。remote_server()
函数设定Fabric的env
环境变量,例如主机名、用户名和密码等。
另一个函数install_package()
调用run()
方法,其参数是在命令行中输入的命令。在这个脚本中,执行的命令是pip install yolk
,即使用pip
安装Python包yolk
。和前面的攻略相比,使用Fabric在远程主机中运行命令更简单也更高效。
7.6 在远程主机中运行MySQL命令
如果你需要远程管理MySQL服务器,可以参考这个攻略。这个攻略会告诉你如何在Python脚本中向远程MySQL服务器发送数据库命令。如果要设置一个使用数据库后台的Web应用,这个攻略可以作为设置Web应用过程中的一部分。
7.6.1 准备工作
这个攻略也需要先安装Fabric。你可以使用Python包管理工具pip
或easy_install
安装,如下面的命令所示。Fabric依赖于paramiko
模块,安装Fabric时会自动安装paramiko
。
$ pip install fabric
这里,我们要通过SSH协议连接远程主机,所以必须在远程主机中运行SSH服务器。远程主机中还要运行MySQL服务器。在Debian/Ubuntu中,可以使用包管理器apt-get
安装openssh
和mysql
服务器,如下面的命令所示:
$ sudo apt-get install openssh-server mysql-server
7.6.2 实战演练
我们要设定一些Fabric环境变量,再定义几个用于远程管理MySQL的函数。在这些函数中,我们不会直接使用可执行文件mysql
,而是通过echo
把SQL命令发送给mysql
。这么做能确保正确地把参数传递给mysql
可执行文件。
代码清单7-5给出了远程运行MySQL命令所需的代码,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 7
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
from getpass import getpass
from fabric.api import run, env, prompt, cd
def remote_server():
# Edit this list to include remote hosts
env.hosts = ['127.0.0.1']
env.user = prompt('Enter your system username: ')
env.password = getpass('Enter your system user password: ')
env.mysqlhost = 'localhost'
env.mysqluser = prompt('Enter your db username: ')
env.mysqlpassword = getpass('Enter your db user password: ')
env.db_name = ''
def show_dbs():
""" Wraps mysql show databases cmd"""
q = "show databases"
run("echo '%s' | mysql -u%s -p%s" %(q, env.mysqluser, env.mysqlpassword))
def run_sql(db_name, query):
""" Generic function to run sql"""
with cd('tmp'):
run("echo '%s' | mysql -u%s -p%s -D %s" %(query, env.mysqluser, env.mysqlpassword, db_name))
def create_db():
"""Create a MySQL DB for App version"""
if not env.db_name:
db_name = prompt("Enter the DB name:")
else:
db_name = env.db_name
run('echo "CREATE DATABASE %s default character set utf8 collate utf8_unicode_ci;"|mysql --batch --user=%s --password=%s --host=%s'\
% (db_name, env.mysqluser, env.mysqlpassword, env.mysqlhost), pty=True)
def ls_db():
""" List a dbs with size in MB """
if not env.db_name:
db_name = prompt("Which DB to ls?")
else:
db_name = env.db_name
query = """SELECT table_schema "DB Name",
Round(Sum(data_length + index_length) 1024 / 1024, 1) "DB Size in MB"
FROM information_schema.tables
WHERE table_schema = \"%s\"
GROUP BY table_schema """ %db_name
run_sql(db_name, query)
def empty_db():
""" Empty all tables of a given DB """
db_name = prompt("Enter DB name to empty:")
cmd = """
(echo 'SET foreign_key_checks = 0;';
(mysqldump -u%s -p%s --add-drop-table --no-data %s |
grep ^DROP);
echo 'SET foreign_key_checks = 1;') | \
mysql -u%s -p%s -b %s
""" %(env.mysqluser, env.mysqlpassword, db_name, env.mysqluser, env.mysqlpassword, db_name)
run(cmd)
若想运行这个脚本,要创建一个快捷方式fabfile.py。在命令行中执行下面的命令可以完成这一操作:
$ ln -sfn 7_5_run_mysql_command_remotely.py fabfile.py
然后,可以使用fab
可执行文件执行不同的操作。
下述命令显示了一个数据库列表(使用SQL查询show databases
):
$ fab remote_server show_dbs
下述命令会创建一个新的MySQL数据库。如果没有定义Fabric环境变量db_name
,会显示一个提示符,要求输入目标数据库的名称。数据库使用以下SQL命令创建:CREATE DATABASE
。
$ fab remote_server create_db
这个Fabric命令显示数据库的大小:
$ fab remote_server ls_db()
下面这个Fabric命令使用可执行文件mysqldump
和mysql
清空数据库。这个函数的作用和数据库的TRUNCATE
命令类似,只不过同时还会删除所有表。结果就像新建一个没有任何表的数据库一样。
$ fab remote_server empty_db()
各命令的输出如下:
$ fab remote_server show_dbs
[127.0.0.1] Executing task 'show_dbs'
[127.0.0.1] run: echo 'show databases' | mysql -uroot -p<DELETED>
[127.0.0.1] out: Database
[127.0.0.1] out: information_schema
[127.0.0.1] out: mysql
[127.0.0.1] out: phpmyadmin
[127.0.0.1] out:
Done.
Disconnecting from 127.0.0.1... done.
$ fab remote_server create_db
[127.0.0.1] Executing task 'create_db'
Enter the DB name: test123
[127.0.0.1] run: echo "CREATE DATABASE test123 default character set utf8
collate utf8_unicode_ci;"|mysql --batch --user=root --password=<DELETED>
--host=localhost
Done.
Disconnecting from 127.0.0.1... done.
$ fab remote_server show_dbs
[127.0.0.1] Executing task 'show_dbs'
[127.0.0.1] run: echo 'show databases' | mysql -uroot -p<DELETED>
[127.0.0.1] out: Database
[127.0.0.1] out: information_schema
[127.0.0.1] out: collabtive
[127.0.0.1] out: test123
[127.0.0.1] out: testdb
[127.0.0.1] out:
Done.
Disconnecting from 127.0.0.1... done.
7.6.3 原理分析
这个脚本定义了Fabric使用的几个函数。第一个函数remote_server()
设定环境变量。本地回送IP(127.0.0.1)保存在主机列表中。本地系统的用户密码和MySQL登录密码通过getpass()
方法获取。
另一个函数利用Fabric中的run()
函数,把MySQL命令回显给mysql
可执行文件,发送给远程MySQL服务器。
run_sql()
函数是个通用函数,作为一个包装函数,可在其他函数中使用。例如,在empty_db()
函数中调用了run_sql()
函数执行SQL命令。这么做可以让代码变得更有序、更简洁。
7.7 通过SSH把文件传输到远程设备中
使用Fabric执行远程系统管理任务时,如果想通过SSH把本地设备中的文件传输到远程设备中,可以使用Fabric内置的get()
和put()
函数。这个攻略向你展示如何定义函数传输文件,并且在传输前后检查硬盘空间。
7.7.1 准备工作
这个攻略也需要先安装Fabric。你可以使用Python包管理工具pip
或easy_install
安装,如下面的命令所示:
$ pip install fabric
这里,我们要通过SSH协议连接远程主机,所以必须在远程主机中安装并运行SSH服务器。
7.7.2 实战演练
我们先要为Fabric设定环境变量,然后再定义两个函数,一个用于下载文件,另一个用于上传文件。
代码清单7-6是通过SSH把文件传输到远程设备中所需的代码,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 7
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
from getpass import getpass
from fabric.api import local, run, env, get, put, prompt, open_shell
def remote_server():
env.hosts = ['127.0.0.1']
env.password = getpass('Enter your system password: ')
env.home_folder = '/tmp'
def login():
open_shell(command="cd %s" %env.home_folder)
def download_file():
print "Checking local disk space..."
local("df -h")
remote_path = prompt("Enter the remote file path:")
local_path = prompt("Enter the local file path:")
get(remote_path=remote_path, local_path=local_path)
local("ls %s" %local_path)
def upload_file():
print "Checking remote disk space..."
run("df -h")
local_path = prompt("Enter the local file path:")
remote_path = prompt("Enter the remote file path:")
put(remote_path=remote_path, local_path=local_path)
run("ls %s" %remote_path)
若想运行这个脚本,要创建一个快捷方式fabfile.py。在命令行中执行下面的命令可以完成这一操作:
$ ln -sfn 7_6_transfer_file_over_ssh.py fabfile.py
然后,可以使用fab
可执行文件执行不同的操作。
首先,若想使用这个脚本登录远程服务器,可以运行下面这个Fabric函数:
$ fab remote_server login
执行上述命令后会看到一个类似shell的微型环境。然后,可以使用下面的命令把文件从远程服务器下载到本地设备中:
$ fab remote_server download_file
类似地,上传文件可以使用下面这个命令:
$ fab remote_server upload_file
在这个例子中,我们通过SSH连接本地设备。因此,你要在本地设备中安装SSH服务器才能运行这个脚本。不然,你可以修改remote_server()
函数,改成连接到远程主机,如下所示:
$ fab remote_server login
[127.0.0.1] Executing task 'login'
Linux debian6 2.6.32-5-686 #1 SMP Mon Feb 25 01:04:36 UTC 2013 i686
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in usrshare/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have new mail.
Last login: Wed Aug 21 15:08:45 2013 from localhost
cd tmp
faruq@debian6:~$ cd tmp
faruq@debian6:/tmp$
<CTRL+D>
faruq@debian6:/tmp$ logout
Done.
Disconnecting from 127.0.0.1... done.
$ fab remote_server download_file
[127.0.0.1] Executing task 'download_file'
Checking local disk space...
[localhost] local: df -h
Filesystem Size Used Avail Use% Mounted on
devsda1 62G 47G 12G 81%
tmpfs 506M 0 506M 0% lib/init/rw
udev 501M 160K 501M 1% /dev
tmpfs 506M 408K 505M 1% devshm
Z_DRIVE 1012G 944G 69G 94% mediaz
C_DRIVE 466G 248G 218G 54% mediac
Enter the remote file path: tmpop.txt
Enter the local file path: .
[127.0.0.1] download: chapter7/op.txt <- tmpop.txt
[localhost] local: ls .
7_1_execute_remote_telnet_cmd.py 7_3_print_remote_cpu_info.py
7_5_run_mysql_command_remotely.py 7_7_configure_Apache_for_hosting_
website_remotely.py fabfile.pyc __init__.py test.txt
7_2_copy_remote_file_over_sftp.py 7_4_install_python_package_
remotely.py 7_6_transfer_file_over_ssh.py fabfile.py
index.html op.txt vhost.conf
Done.
Disconnecting from 127.0.0.1... done.
7.7.3 原理分析
在这个攻略中,用到了几个Fabric内置的函数,在本地设备和远程设备之间传输文件。local()
函数在本地设备中执行操作,run()
函数在远程设备中执行操作。
在上传和下载文件前最好先检查目标设备中的可用硬盘空间。
这个操作使用Unix命令df
实现。源文件和目标文件的路径可以在命令行中指定,也可硬编码在源文件中,以备无人值守时自动执行。
7.8 远程配置Apache运行网站
Fabric函数可以以普通用户身份运行,也可以超级用户身份运行。如果想在远程Apache服务器上运行网站,需要管理员权限才能创建配置文件以及重启Web服务器。这个攻略介绍Fabric的sudo()
函数,以超级用户身份在远程设备中执行命令。这里,我们要配置运行网站所需的Apache虚拟主机。
7.8.1 准备工作
这个攻略需要先在本地设备中安装Fabric。你可以使用Python包管理工具pip
或easy_install
安装,如下面的命令所示:
$ pip install fabric
这里,我们要通过SSH协议连接远程主机,所以必须在远程主机中安装并运行SSH服务器。远程主机中还需要安装和运行Apache Web服务器。在Debian/Ubuntu中,可以使用包管理器apt-get
安装,如下面的命令所示:
$ sudo apt-get install openssh-server apache2
7.8.2 实战演练
首先,我们要知道Apache的安装路径和一些配置参数,例如Web服务器的用户、用户组、虚拟主机配置文件的路径和初始化脚本。这些参数可以定义为常量。
然后,定义两个函数,remote_server()
和setup_vhost()
,使用Fabric执行Apache配置任务。
代码清单7-7是远程配置Apache运行网站所需的代码,如下所示:
#!usrbin/env python
# Python Network Programming Cookbook -- Chapter - 7
# This program is optimized for Python 2.7.
# It may run on any other version with/without modifications.
from getpass import getpass
from fabric.api import env, put, sudo, prompt
from fabric.contrib.files import exists
WWW_DOC_ROOT = "dataapache/test/"
WWW_USER = "www-data"
WWW_GROUP = "www-data"
APACHE_SITES_PATH = "etcapache2/sites-enabled/"
APACHE_INIT_SCRIPT = "etcinit.d/apache2 "
def remote_server():
env.hosts = ['127.0.0.1']
env.user = prompt('Enter user name: ')
env.password = getpass('Enter your system password: ')
def setup_vhost():
""" Setup a test website """
print "Preparing the Apache vhost setup..."
print "Setting up the document root..."
if exists(WWW_DOC_ROOT):
sudo("rm -rf %s" %WWW_DOC_ROOT)
sudo("mkdir -p %s" %WWW_DOC_ROOT)
# setup file permissions
sudo("chown -R %s.%s %s" %(env.user, env.user, WWW_DOC_ROOT))
# upload a sample index.html file
put(local_path="index.html", remote_path=WWW_DOC_ROOT)
sudo("chown -R %s.%s %s" %(WWW_USER, WWW_GROUP, WWW_DOC_ROOT))
print "Setting up the vhost..."
sudo("chown -R %s.%s %s" %(env.user, env.user, APACHE_SITES_PATH))
# upload a pre-configured vhost.conf
put(local_path="vhost.conf", remote_path=APACHE_SITES_PATH)
sudo("chown -R %s.%s %s" %('root', 'root', APACHE_SITES_PATH))
# restart Apache to take effect
sudo("%s restart" %APACHE_INIT_SCRIPT)
print "Setup complete. Now open the server path http://abc.remote-server.org/ in your web browser."
为了运行这个脚本,要把下面这行加入主机文件中,例如etchosts:
127.0.0.1 abc.remote-server.org abc
还要创建快捷方式fabfile.py。在命令行中,可以使用下面的命令完成:
$ ln -sfn 7_7_configure_Apache_for_hosting_website_remotely.py fabfile.py
然后,可以使用fab
可执行文件执行不同的操作。
首先,若想使用这个脚本登录远程服务器,可以运行下面这个Fabric函数。得到的输出结果如下:
$ fab remote_server setup_vhost
[127.0.0.1] Executing task 'setup_vhost'
Preparing the Apache vhost setup...
Setting up the document root...
[127.0.0.1] sudo: rm -rf dataapache/test/
[127.0.0.1] sudo: mkdir -p dataapache/test/
[127.0.0.1] sudo: chown -R faruq.faruq dataapache/test/
[127.0.0.1] put: index.html -> dataapache/test/index.html
[127.0.0.1] sudo: chown -R www-data.www-data dataapache/test/
Setting up the vhost...
[127.0.0.1] sudo: chown -R faruq.faruq etcapache2/sites-enabled/
[127.0.0.1] put: vhost.conf -> etcapache2/sites-enabled/vhost.conf
[127.0.0.1] sudo: chown -R root.root etcapache2/sites-enabled/
[127.0.0.1] sudo: etcinit.d/apache2 restart
[127.0.0.1] out: Restarting web server: apache2apache2: Could not
reliably determine the server's fully qualified domain name, using
127.0.0.1 for ServerName
[127.0.0.1] out: ... waiting apache2: Could not reliably determine the
server's fully qualified domain name, using 127.0.0.1 for ServerName
[127.0.0.1] out: .
[127.0.0.1] out:
Setup complete. Now open the server path http://abc.remote-server.org/ in your web browser.
Done.
Disconnecting from 127.0.0.1... done.
运行这个脚本之后,你可以打开浏览器,访问主机文件(例如etchosts)中设定的路径。在浏览器中会看到如下输出:
It works!
This is the default web page for this server.
The web server software is running but no content has been added, yet.
7.8.3 原理分析
这个攻略把初始的Apache配置参数设置为常量,然后定义了两个函数。在remote_server()
函数中,和往常一样,设定了Fabric环境参数,例如主机、用户名和密码等。
setup_vhost()
函数执行了一系列需要特殊权限的命令。首先,使用exists()
函数检查是否已经创建了网站的文档根目录。如果该路径存在,就将其删除,在下一步中重新创建。然后再执行chown
命令,确保当前用户有权访问这个路径。
接着,把一个骨架HTML文件(index.html)上传到文档根路径中。上传后,再把文件的访问权限赋予Web服务器用户。
设置好文档根目录后,setup_vhost()
函数把vhost.conf文件上传到Apache的网站配置路径中,然后把这个文件的拥有者设为根用户。
最后,这个脚本重启Apache服务器,让配置生效。如果配置正确,你会在浏览器中看到前面访问http://abc.remote-server.org/时显示的内容。