第 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-getaptitude安装telnetd包,如下面的命令所示:

  1. $ sudo apt-get install telnetd
  2. $ telnet localhost

7.2.2 实战演练

我们来定义一个函数,从命令行中获取用户登录凭据,然后连接telnet服务器。

成功连接后,这个函数会把ls命令发送给服务器,然后显示命令的输出,例如列出一个目录中的内容。

代码清单7-1是一个telnet会话的代码,在远程主机中执行一个Unix命令,如下所示:

  1. #!usrbin/env python
  2. # Python Network Programming Cookbook -- Chapter - 7
  3. # This program is optimized for Python 2.7.
  4. # It may run on any other version with/without modifications.
  5. import getpass
  6. import sys
  7. import telnetlib
  8. HOST = "localhost"
  9. def run_telnet_session():
  10. user = raw_input("Enter your remote account: ")
  11. password = getpass.getpass()
  12. session = telnetlib.Telnet(HOST)
  13. session.read_until("login: ")
  14. session.write(user + "\n")
  15. if password:
  16. session.read_until("Password: ")
  17. session.write(password + "\n")
  18. session.write("ls\n")
  19. session.write("exit\n")
  20. print session.read_all()
  21. if __name__ == '__main__':
  22. run_telnet_session()

如果本地设备中运行有telnet服务器,运行这个脚本后,会要求你输入远程主机的用户账户和密码。在Debian设备中执行这个telnet会话得到的输出如下所示:

  1. $ python 7_1_execute_remote_telnet_cmd.py
  2. Enter remote hostname e.g. localhost: localhost
  3. Enter your remote account: faruq
  4. Password:
  5. ls
  6. exit
  7. Last login: Mon Aug 12 10:37:10 BST 2013 from localhost on pts/9
  8. Linux debian6 2.6.32-5-686 #1 SMP Mon Feb 25 01:04:36 UTC 2013 i686
  9. The programs included with the Debian GNU/Linux system are free software;
  10. the exact distribution terms for each program are described in the
  11. individual files in usrshare/doc/*/copyright.
  12. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
  13. permitted by applicable law.
  14. You have new mail.
  15. faruq@debian6:~$ ls
  16. down Pictures Videos
  17. Downloads projects yEd
  18. Dropbox Public
  19. env readme.txt
  20. faruq@debian6:~$ exit
  21. 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安装:

  1. $ pip install paramiko

7.3.2 实战演练

这个攻略要从命令行中接收一些输入值,包括远程主机名、服务器端口、源文件名、目标文件名。简单起见,我们可以使用默认值或者硬编码的值。

连接远程服务器需要用户名和密码,这两个值可以从用户在命令行中的输入获取。

代码清单7-2说明了如何通过SFTP把文件复制到远程主机中,如下所示:

  1. #!usrbin/env python
  2. # Python Network Programming Cookbook -- Chapter - 7
  3. # This program is optimized for Python 2.7.
  4. # It may run on any other version with/without modifications.
  5. import argparse
  6. import paramiko
  7. import getpass
  8. SOURCE = '7_2_copy_remote_file_over_sftp.py'
  9. DESTINATION ='tmp7_2_copy_remote_file_over_sftp.py '
  10. def copy_file(hostname, port, username, password, src, dst):
  11. client = paramiko.SSHClient()
  12. client.load_system_host_keys()
  13. print " Connecting to %s \n with username=%s... \n" %(hostname,username)
  14. t = paramiko.Transport((hostname, port))
  15. t.connect(username=username,password=password)
  16. sftp = paramiko.SFTPClient.from_transport(t)
  17. print "Copying file: %s to path: %s" %(src, dst)
  18. sftp.put(src, dst)
  19. sftp.close()
  20. t.close()
  21. if __name__ == '__main__':
  22. parser = argparse.ArgumentParser(description='Remote file copy')
  23. parser.add_argument('--host', action="store", dest="host", default='localhost')
  24. parser.add_argument('--port', action="store", dest="port", default=22, type=int)
  25. parser.add_argument('--src', action="store", dest="src", default=SOURCE)
  26. parser.add_argument('--dst', action="store", dest="dst", default=DESTINATION)
  27. given_args = parser.parse_args()
  28. hostname, port = given_args.host, given_args.port
  29. src, dst = given_args.src, given_args.dst
  30. username = raw_input("Enter the username:")
  31. password = getpass.getpass("Enter password for %s: " %username)
  32. copy_file(hostname, port, username, password, src, dst)

运行这个脚本后,会看到类似下面的输出:

  1. $ python 7_2_copy_remote_file_over_sftp.py
  2. Enter the username:faruq
  3. Enter password for faruq:
  4. Connecting to localhost
  5. with username=faruq...
  6. Copying file: 7_2_copy_remote_file_over_sftp.py to path:
  7. tmp7_2_copy_remote_file_over_sftp.py

7.3.3 原理分析

这个攻略可以接收不同的输入值,然后连接到远程设备,通过SFTP复制文件。

这个攻略把命令行中的输入传给copy_file()函数,然后使用paramiko库中的SSHClient类创建一个SSH客户端。这个客户端需要加载系统的主机密钥。然后创建一个Transport类的实例,连接远程服务器。真正的SFTP连接对象sftpparamiko库中的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

  1. $ pip install paramiko

7.4.2 实战演练

我们可以使用paramiko模块创建一个远程会话连接Unix设备。

然后,通过这个会话,我们可以读取远程设备中的proccpuinfo文件,获取CPU信息。

代码清单7-3是打印远程设备CPU信息的代码,如下所示:

  1. #!usrbin/env python
  2. # Python Network Programming Cookbook -- Chapter - 7
  3. # This program is optimized for Python 2.7.
  4. # It may run on any other version with/without modifications.
  5. import argparse
  6. import getpass
  7. import paramiko
  8. RECV_BYTES = 4096
  9. COMMAND = 'cat proccpuinfo'
  10. def print_remote_cpu_info(hostname, port, username, password):
  11. client = paramiko.Transport((hostname, port))
  12. client.connect(username=username, password=password)
  13. stdout_data = []
  14. stderr_data = []
  15. session = client.open_channel(kind='session')
  16. session.exec_command(COMMAND)
  17. while True:
  18. if session.recv_ready():
  19. stdout_data.append(session.recv(RECV_BYTES))
  20. if session.recv_stderr_ready():
  21. stderr_data.append(session.recv_stderr(RECV_BYTES))
  22. if session.exit_status_ready():
  23. break
  24. print 'exit status: ', session.recv_exit_status()
  25. print ''.join(stdout_data)
  26. print ''.join(stderr_data)
  27. session.close()
  28. client.close()
  29. if __name__ == '__main__':
  30. parser = argparse.ArgumentParser(description='Remote file copy')
  31. parser.add_argument('--host', action="store", dest="host", default='localhost')
  32. parser.add_argument('--port', action="store", dest="port", default=22, type=int)
  33. given_args = parser.parse_args()
  34. hostname, port = given_args.host, given_args.port
  35. username = raw_input("Enter the username:")
  36. password = getpass.getpass("Enter password for %s: " %username)
  37. print_remote_cpu_info(hostname, port, username, password)

运行这个脚本后会显示指定主机的CPU信息,这里连接的是本地设备,如下所示:

  1. $ python 7_3_print_remote_cpu_info.py
  2. Enter the username:faruq
  3. Enter password for faruq:
  4. exit status: 0
  5. processor: 0
  6. vendor_id: GenuineIntel
  7. cpu family: 6
  8. model: 42
  9. model name: Intel(R) Core(TM) i5-2400S CPU @ 2.50GHz
  10. stepping: 7
  11. cpu MHz: 2469.677
  12. cache size: 6144 KB
  13. fdiv_bug: no
  14. hlt_bug: no
  15. f00f_bug: no
  16. coma_bug: no
  17. fpu: yes
  18. fpu_exception: yes
  19. cpuid level: 5
  20. wp: yes
  21. flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca
  22. cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx rdtscp lm constant_
  23. tsc up pni monitor ssse3 lahf_lm
  24. bogomips: 4939.35
  25. clflush size: 64
  26. cache_alignment: 64
  27. address sizes: 36 bits physical, 48 bits virtual
  28. power management:

7.4.3 原理分析

首先,收集连接所需的参数,例如主机名、端口号、用户名和密码。然后,把这些参数传给print_remote_cpu_info()函数。

在这个函数中,使用paramiko模块中的Transport类创建了一个SSH客户端会话。然后使用提供的用户名和密码连接远程设备。我们可以在SSH客户端上调用open_channel()方法创建一个原始通信会话。若想在远程主机中执行命令,可以使用exec_command()方法。

把命令发送给远程主机之后,可以通过封阻会话对象的recv_ready()事件来获取远程主机的响应。我们可以创建两个列表,stdout_datastderr_data,用来存储远程主机的输出和错误消息。

命令在远程设备中退出时,可以使用exit_status_ready()方法检测到。接收到的远程会话数据使用join()方法串接起来。

最后,分别在各对象上调用close()方法,中断会话和客户端连接。

7.5 在远程主机中安装Python包

在前面几个攻略中,你可能注意到了,在远程主机中执行操作时,要写很多代码建立连接。为了提高执行效率,最好抽象这些代码,只向程序员开放相对高级的操作。在远程设备中执行命令时总是要建立连接,这个过程很繁琐,也浪费时间。

Fabric(http://fabfile.org/)这个Python第三方模块可以解决这个问题。它只开放了适当数量的API,能高效地和远程设备交互。

这个攻略举个简单的例子说明如何使用Fabric。

7.5.1 准备工作

首先,我们要安装Fabric。你可以使用Python包管理工具pipeasy_install安装,如下面的命令所示。Fabric依赖于paramiko模块,安装Fabric时会自动安装paramiko

  1. $ pip install fabric

这里,我们要通过SSH协议连接远程主机,所以必须在远程主机中运行SSH服务器。如果想在本机中测试(假装是连接到远程设备),可以在本地安装openssh服务器包。在Debian/Ubuntu中,可以使用包管理器apt-get安装,如下面的命令所示:

  1. $ sudo apt-get install openssh-server

7.5.2 实战演练

下面这段代码说明了如何使用Fabric安装Python包。

代码清单7-4给出了在远程主机中安装Python包所需的代码,如下所示:

  1. #!usrbin/env python
  2. # Python Network Programming Cookbook -- Chapter - 7
  3. # This program is optimized for Python 2.7.
  4. # It may run on any other version with/without modifications.
  5. from getpass import getpass
  6. from fabric.api import settings, run, env, prompt
  7. def remote_server():
  8. env.hosts = ['127.0.0.1']
  9. env.user = prompt('Enter user name: ')
  10. env.password = getpass('Enter password: ')
  11. def install_package():
  12. 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的快捷方式:

  1. $ rm -rf fabfile.py
  2. $ ln -s 7_4_install_python_package_remotely.py fabfile.py

如果运行fabfile,在远程主机中安装Python包yolk后会生成如下输出:

  1. $ ln -sfn 7_4_install_python_package_remotely.py fabfile.py
  2. $ fab remote_server install_package
  3. Enter user name: faruq
  4. Enter password:
  5. [127.0.0.1] Executing task 'install_package'
  6. [127.0.0.1] run: pip install yolk
  7. [127.0.0.1] out: Downloading/unpacking yolk
  8. [127.0.0.1] out: Downloading yolk-0.4.3.tar.gz (86kB):
  9. [127.0.0.1] out: Downloading yolk-0.4.3.tar.gz (86kB): 100% 86kB
  10. [127.0.0.1] out: Downloading yolk-0.4.3.tar.gz (86kB):
  11. [127.0.0.1] out: Downloading yolk-0.4.3.tar.gz (86kB): 86kB
  12. downloaded
  13. [127.0.0.1] out: Running setup.py egg_info for package yolk
  14. [127.0.0.1] out: Installing yolk script to homefaruq/env/bin
  15. [127.0.0.1] out: Successfully installed yolk
  16. [127.0.0.1] out: Cleaning up...
  17. [127.0.0.1] out:
  18. Done.
  19. 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包管理工具pipeasy_install安装,如下面的命令所示。Fabric依赖于paramiko模块,安装Fabric时会自动安装paramiko

  1. $ pip install fabric

这里,我们要通过SSH协议连接远程主机,所以必须在远程主机中运行SSH服务器。远程主机中还要运行MySQL服务器。在Debian/Ubuntu中,可以使用包管理器apt-get安装opensshmysql服务器,如下面的命令所示:

  1. $ sudo apt-get install openssh-server mysql-server

7.6.2 实战演练

我们要设定一些Fabric环境变量,再定义几个用于远程管理MySQL的函数。在这些函数中,我们不会直接使用可执行文件mysql,而是通过echo把SQL命令发送给mysql。这么做能确保正确地把参数传递给mysql可执行文件。

代码清单7-5给出了远程运行MySQL命令所需的代码,如下所示:

  1. #!usrbin/env python
  2. # Python Network Programming Cookbook -- Chapter - 7
  3. # This program is optimized for Python 2.7.
  4. # It may run on any other version with/without modifications.
  5. from getpass import getpass
  6. from fabric.api import run, env, prompt, cd
  7. def remote_server():
  8. # Edit this list to include remote hosts
  9. env.hosts = ['127.0.0.1']
  10. env.user = prompt('Enter your system username: ')
  11. env.password = getpass('Enter your system user password: ')
  12. env.mysqlhost = 'localhost'
  13. env.mysqluser = prompt('Enter your db username: ')
  14. env.mysqlpassword = getpass('Enter your db user password: ')
  15. env.db_name = ''
  16. def show_dbs():
  17. """ Wraps mysql show databases cmd"""
  18. q = "show databases"
  19. run("echo '%s' | mysql -u%s -p%s" %(q, env.mysqluser, env.mysqlpassword))
  20. def run_sql(db_name, query):
  21. """ Generic function to run sql"""
  22. with cd('tmp'):
  23. run("echo '%s' | mysql -u%s -p%s -D %s" %(query, env.mysqluser, env.mysqlpassword, db_name))
  24. def create_db():
  25. """Create a MySQL DB for App version"""
  26. if not env.db_name:
  27. db_name = prompt("Enter the DB name:")
  28. else:
  29. db_name = env.db_name
  30. run('echo "CREATE DATABASE %s default character set utf8 collate utf8_unicode_ci;"|mysql --batch --user=%s --password=%s --host=%s'\
  31. % (db_name, env.mysqluser, env.mysqlpassword, env.mysqlhost), pty=True)
  32. def ls_db():
  33. """ List a dbs with size in MB """
  34. if not env.db_name:
  35. db_name = prompt("Which DB to ls?")
  36. else:
  37. db_name = env.db_name
  38. query = """SELECT table_schema "DB Name",
  39. Round(Sum(data_length + index_length) 1024 / 1024, 1) "DB Size in MB"
  40. FROM information_schema.tables
  41. WHERE table_schema = \"%s\"
  42. GROUP BY table_schema """ %db_name
  43. run_sql(db_name, query)
  44. def empty_db():
  45. """ Empty all tables of a given DB """
  46. db_name = prompt("Enter DB name to empty:")
  47. cmd = """
  48. (echo 'SET foreign_key_checks = 0;';
  49. (mysqldump -u%s -p%s --add-drop-table --no-data %s |
  50. grep ^DROP);
  51. echo 'SET foreign_key_checks = 1;') | \
  52. mysql -u%s -p%s -b %s
  53. """ %(env.mysqluser, env.mysqlpassword, db_name, env.mysqluser, env.mysqlpassword, db_name)
  54. run(cmd)

若想运行这个脚本,要创建一个快捷方式fabfile.py。在命令行中执行下面的命令可以完成这一操作:

  1. $ ln -sfn 7_5_run_mysql_command_remotely.py fabfile.py

然后,可以使用fab可执行文件执行不同的操作。

下述命令显示了一个数据库列表(使用SQL查询show databases):

  1. $ fab remote_server show_dbs

下述命令会创建一个新的MySQL数据库。如果没有定义Fabric环境变量db_name,会显示一个提示符,要求输入目标数据库的名称。数据库使用以下SQL命令创建:CREATE DATABASE default character set utf8 collate utf8_unicode_ci;

  1. $ fab remote_server create_db

这个Fabric命令显示数据库的大小:

  1. $ fab remote_server ls_db()

下面这个Fabric命令使用可执行文件mysqldumpmysql清空数据库。这个函数的作用和数据库的TRUNCATE命令类似,只不过同时还会删除所有表。结果就像新建一个没有任何表的数据库一样。

  1. $ fab remote_server empty_db()

各命令的输出如下:

  1. $ fab remote_server show_dbs
  2. [127.0.0.1] Executing task 'show_dbs'
  3. [127.0.0.1] run: echo 'show databases' | mysql -uroot -p<DELETED>
  4. [127.0.0.1] out: Database
  5. [127.0.0.1] out: information_schema
  6. [127.0.0.1] out: mysql
  7. [127.0.0.1] out: phpmyadmin
  8. [127.0.0.1] out:
  9. Done.
  10. Disconnecting from 127.0.0.1... done.
  11. $ fab remote_server create_db
  12. [127.0.0.1] Executing task 'create_db'
  13. Enter the DB name: test123
  14. [127.0.0.1] run: echo "CREATE DATABASE test123 default character set utf8
  15. collate utf8_unicode_ci;"|mysql --batch --user=root --password=<DELETED>
  16. --host=localhost
  17. Done.
  18. Disconnecting from 127.0.0.1... done.
  19. $ fab remote_server show_dbs
  20. [127.0.0.1] Executing task 'show_dbs'
  21. [127.0.0.1] run: echo 'show databases' | mysql -uroot -p<DELETED>
  22. [127.0.0.1] out: Database
  23. [127.0.0.1] out: information_schema
  24. [127.0.0.1] out: collabtive
  25. [127.0.0.1] out: test123
  26. [127.0.0.1] out: testdb
  27. [127.0.0.1] out:
  28. Done.
  29. 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包管理工具pipeasy_install安装,如下面的命令所示:

  1. $ pip install fabric

这里,我们要通过SSH协议连接远程主机,所以必须在远程主机中安装并运行SSH服务器。

7.7.2 实战演练

我们先要为Fabric设定环境变量,然后再定义两个函数,一个用于下载文件,另一个用于上传文件。

代码清单7-6是通过SSH把文件传输到远程设备中所需的代码,如下所示:

  1. #!usrbin/env python
  2. # Python Network Programming Cookbook -- Chapter - 7
  3. # This program is optimized for Python 2.7.
  4. # It may run on any other version with/without modifications.
  5. from getpass import getpass
  6. from fabric.api import local, run, env, get, put, prompt, open_shell
  7. def remote_server():
  8. env.hosts = ['127.0.0.1']
  9. env.password = getpass('Enter your system password: ')
  10. env.home_folder = '/tmp'
  11. def login():
  12. open_shell(command="cd %s" %env.home_folder)
  13. def download_file():
  14. print "Checking local disk space..."
  15. local("df -h")
  16. remote_path = prompt("Enter the remote file path:")
  17. local_path = prompt("Enter the local file path:")
  18. get(remote_path=remote_path, local_path=local_path)
  19. local("ls %s" %local_path)
  20. def upload_file():
  21. print "Checking remote disk space..."
  22. run("df -h")
  23. local_path = prompt("Enter the local file path:")
  24. remote_path = prompt("Enter the remote file path:")
  25. put(remote_path=remote_path, local_path=local_path)
  26. run("ls %s" %remote_path)

若想运行这个脚本,要创建一个快捷方式fabfile.py。在命令行中执行下面的命令可以完成这一操作:

  1. $ ln -sfn 7_6_transfer_file_over_ssh.py fabfile.py

然后,可以使用fab可执行文件执行不同的操作。

首先,若想使用这个脚本登录远程服务器,可以运行下面这个Fabric函数:

  1. $ fab remote_server login

执行上述命令后会看到一个类似shell的微型环境。然后,可以使用下面的命令把文件从远程服务器下载到本地设备中:

  1. $ fab remote_server download_file

类似地,上传文件可以使用下面这个命令:

  1. $ fab remote_server upload_file

在这个例子中,我们通过SSH连接本地设备。因此,你要在本地设备中安装SSH服务器才能运行这个脚本。不然,你可以修改remote_server()函数,改成连接到远程主机,如下所示:

  1. $ fab remote_server login
  2. [127.0.0.1] Executing task 'login'
  3. Linux debian6 2.6.32-5-686 #1 SMP Mon Feb 25 01:04:36 UTC 2013 i686
  4. The programs included with the Debian GNU/Linux system are free software;
  5. the exact distribution terms for each program are described in the
  6. individual files in usrshare/doc/*/copyright.
  7. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
  8. permitted by applicable law.
  9. You have new mail.
  10. Last login: Wed Aug 21 15:08:45 2013 from localhost
  11. cd tmp
  12. faruq@debian6:~$ cd tmp
  13. faruq@debian6:/tmp$
  14. <CTRL+D>
  15. faruq@debian6:/tmp$ logout
  16. Done.
  17. Disconnecting from 127.0.0.1... done.
  18. $ fab remote_server download_file
  19. [127.0.0.1] Executing task 'download_file'
  20. Checking local disk space...
  21. [localhost] local: df -h
  22. Filesystem Size Used Avail Use% Mounted on
  23. devsda1 62G 47G 12G 81%
  24. tmpfs 506M 0 506M 0% lib/init/rw
  25. udev 501M 160K 501M 1% /dev
  26. tmpfs 506M 408K 505M 1% devshm
  27. Z_DRIVE 1012G 944G 69G 94% mediaz
  28. C_DRIVE 466G 248G 218G 54% mediac
  29. Enter the remote file path: tmpop.txt
  30. Enter the local file path: .
  31. [127.0.0.1] download: chapter7/op.txt <- tmpop.txt
  32. [localhost] local: ls .
  33. 7_1_execute_remote_telnet_cmd.py 7_3_print_remote_cpu_info.py
  34. 7_5_run_mysql_command_remotely.py 7_7_configure_Apache_for_hosting_
  35. website_remotely.py fabfile.pyc __init__.py test.txt
  36. 7_2_copy_remote_file_over_sftp.py 7_4_install_python_package_
  37. remotely.py 7_6_transfer_file_over_ssh.py fabfile.py
  38. index.html op.txt vhost.conf
  39. Done.
  40. 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包管理工具pipeasy_install安装,如下面的命令所示:

  1. $ pip install fabric

这里,我们要通过SSH协议连接远程主机,所以必须在远程主机中安装并运行SSH服务器。远程主机中还需要安装和运行Apache Web服务器。在Debian/Ubuntu中,可以使用包管理器apt-get安装,如下面的命令所示:

  1. $ sudo apt-get install openssh-server apache2

7.8.2 实战演练

首先,我们要知道Apache的安装路径和一些配置参数,例如Web服务器的用户、用户组、虚拟主机配置文件的路径和初始化脚本。这些参数可以定义为常量。

然后,定义两个函数,remote_server()setup_vhost(),使用Fabric执行Apache配置任务。

代码清单7-7是远程配置Apache运行网站所需的代码,如下所示:

  1. #!usrbin/env python
  2. # Python Network Programming Cookbook -- Chapter - 7
  3. # This program is optimized for Python 2.7.
  4. # It may run on any other version with/without modifications.
  5. from getpass import getpass
  6. from fabric.api import env, put, sudo, prompt
  7. from fabric.contrib.files import exists
  8. WWW_DOC_ROOT = "dataapache/test/"
  9. WWW_USER = "www-data"
  10. WWW_GROUP = "www-data"
  11. APACHE_SITES_PATH = "etcapache2/sites-enabled/"
  12. APACHE_INIT_SCRIPT = "etcinit.d/apache2 "
  13. def remote_server():
  14. env.hosts = ['127.0.0.1']
  15. env.user = prompt('Enter user name: ')
  16. env.password = getpass('Enter your system password: ')
  17. def setup_vhost():
  18. """ Setup a test website """
  19. print "Preparing the Apache vhost setup..."
  20. print "Setting up the document root..."
  21. if exists(WWW_DOC_ROOT):
  22. sudo("rm -rf %s" %WWW_DOC_ROOT)
  23. sudo("mkdir -p %s" %WWW_DOC_ROOT)
  24. # setup file permissions
  25. sudo("chown -R %s.%s %s" %(env.user, env.user, WWW_DOC_ROOT))
  26. # upload a sample index.html file
  27. put(local_path="index.html", remote_path=WWW_DOC_ROOT)
  28. sudo("chown -R %s.%s %s" %(WWW_USER, WWW_GROUP, WWW_DOC_ROOT))
  29. print "Setting up the vhost..."
  30. sudo("chown -R %s.%s %s" %(env.user, env.user, APACHE_SITES_PATH))
  31. # upload a pre-configured vhost.conf
  32. put(local_path="vhost.conf", remote_path=APACHE_SITES_PATH)
  33. sudo("chown -R %s.%s %s" %('root', 'root', APACHE_SITES_PATH))
  34. # restart Apache to take effect
  35. sudo("%s restart" %APACHE_INIT_SCRIPT)
  36. print "Setup complete. Now open the server path http://abc.remote-server.org/ in your web browser."

为了运行这个脚本,要把下面这行加入主机文件中,例如etchosts:

  1. 127.0.0.1 abc.remote-server.org abc

还要创建快捷方式fabfile.py。在命令行中,可以使用下面的命令完成:

  1. $ ln -sfn 7_7_configure_Apache_for_hosting_website_remotely.py fabfile.py

然后,可以使用fab可执行文件执行不同的操作。

首先,若想使用这个脚本登录远程服务器,可以运行下面这个Fabric函数。得到的输出结果如下:

  1. $ fab remote_server setup_vhost
  2. [127.0.0.1] Executing task 'setup_vhost'
  3. Preparing the Apache vhost setup...
  4. Setting up the document root...
  5. [127.0.0.1] sudo: rm -rf dataapache/test/
  6. [127.0.0.1] sudo: mkdir -p dataapache/test/
  7. [127.0.0.1] sudo: chown -R faruq.faruq dataapache/test/
  8. [127.0.0.1] put: index.html -> dataapache/test/index.html
  9. [127.0.0.1] sudo: chown -R www-data.www-data dataapache/test/
  10. Setting up the vhost...
  11. [127.0.0.1] sudo: chown -R faruq.faruq etcapache2/sites-enabled/
  12. [127.0.0.1] put: vhost.conf -> etcapache2/sites-enabled/vhost.conf
  13. [127.0.0.1] sudo: chown -R root.root etcapache2/sites-enabled/
  14. [127.0.0.1] sudo: etcinit.d/apache2 restart
  15. [127.0.0.1] out: Restarting web server: apache2apache2: Could not
  16. reliably determine the server's fully qualified domain name, using
  17. 127.0.0.1 for ServerName
  18. [127.0.0.1] out: ... waiting apache2: Could not reliably determine the
  19. server's fully qualified domain name, using 127.0.0.1 for ServerName
  20. [127.0.0.1] out: .
  21. [127.0.0.1] out:
  22. Setup complete. Now open the server path http://abc.remote-server.org/ in your web browser.
  23. Done.
  24. Disconnecting from 127.0.0.1... done.

运行这个脚本之后,你可以打开浏览器,访问主机文件(例如etchosts)中设定的路径。在浏览器中会看到如下输出:

  1. It works!
  2. This is the default web page for this server.
  3. 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/时显示的内容。