随着分布式架构的普及,程序的升级和维护变得越来越复杂。为了简化这一过程,自动化升级程序成为了一种常见的解决方案。本文将介绍一种基于expect.net技术的自动化程序升级和远程控制工具的实现过程。
在分布式架构下,程序升级往往需要在多个服务器上进行。传统的手动升级方式不仅耗时,而且容易出错。因此,执行预定义的脚本以自动远程更新程序成为了一种有效的解决方案。这种方式可以节省大量的工作和时间。
有许多工具支持自动执行脚本和远程控制机器,如putty、plink和telnet。但它们有一些限制,例如su命令。"Expect"技术克服了这些问题,它将stdin和stdout重定向到程序,就像操作员在终端执行命令一样。
脚本引擎的需求如下:
常用的expect.net API如下:
// 1. 实例化一个可生成进程的ISpawnable对象
ProcessSpawnable(execute_command, command_args)
// 2. 执行ISPawnable进程
Session spawn = Expect.Spawn(ISpawnable);
// 3. 等待远程服务器的响应。如果得到回复,执行第二个参数中定义的匿名函数。否则抛出异常。
spawn.Expect(
"Password:",
(s) => Console.WriteLine("found: " + s)
)
// 4. 设置等待超时
spawn.Timeout = 30000
// 5. 向远程服务器发送命令
spawn.Send()
以"使用plink登录远程Linux服务器"为例,展示expect.net的特点。
// 执行plink
Session spawn = Expect.Spawn(
new ProcessSpawnable(
"c:\\plink.exe",
"-l username -pw password xx.xx.xx.xx -t"
)
)
// 等待远程服务器响应包含"$"的字符串。如果在超时前得到字符串,打印出来。
spawn.Expect(
"$",
s => Console.WriteLine("got: " + s)
)
// 向远程服务器发送"su"命令
spawn.Send("su\n")
// 等待远程服务器响应包含"Password:"的字符串。如果在超时前得到字符串,打印出来。
spawn.Expect(
"Password:",
(s) => Console.WriteLine("found: " + s)
)
// 向外发送密码
spawn.Send("1234\n")
// 等待远程服务器响应包含"root"的字符串。如果在超时前得到字符串,打印出来。
spawn.Expect(
"root",
(s) => Console.WriteLine("found: " + s)
)
// 向外发送chmod命令
spawn.Send("chmod 777 /home/guest\n")
对于"批量升级"工作来说,错误管理非常重要。如果在升级过程中出现问题,工具必须让用户容易且清晰地知道问题所在。
现在设计了一个机制,工具在执行期间会为每个远程服务器本地保留日志。如果执行期间出现问题,日志文件名的尾部会替换为"err"。操作员只需检查文件名带有"err"的文件,就可以知道问题所在。
脚本引擎设计了两个执行参数,它们的含义如下:
脚本的格式如下:
opcode op1 op2
#opcode
是脚本命令的名称
#op1, op2
是opcode的参数。参数的数量可以是一个或两个,取决于opcode。
脚本命令如下:
// 实例化一个名为s1的spawn对象,并执行plink和参数。
spawn s1
"c:\plink.exe",
"-l username -pw password -t"
// 实例化一个名为s2的spawn对象,并执行psftp。
spawn s2
"c:\psftp.exe",
"-l username -pw password "
// 在plink上执行"su",并在得到"Password:"字符串后发送密码"1234"。
s1.expect
"Password:"
"su"
s1.expect
"#"
"1234"
// 在plink上执行"chmod"并检查执行结果。之后等待得到"#"字符串。
s1.expect_with_check
"#"
"chmod 777 /home/guest"
s1.expect_with_check
"#"
"mkdir /home/usb"
// 将USB驱动器挂载到/home/usb
s1.mountusb osusb /home/usb
// 在psftp上执行"put",并等待得到">"字符串,使用expect_longtimeout命令,因为通过ftp上传文件需要更长的时间。
s2.expect_with_check
">"
"cd /home/guest/"
s2.expect_longtimeout
">"
"put c:\test.txt"
// 获取执行execute_pcs时的PID,并将其分配给ncspid对象。
s1.assignpid ncspid execute_pcs
// 杀死存储在ncspid对象中的PID。
s1.kill
"#"
ncspid
// 在plink上执行"cp"命令,并将/home/guest/test.txt复制到/home/usb/test/。
s1.expect_with_check
"#"
"cp /home/guest/test.txt /home/usb/test/"
s1.expect_with_check
"#"
"umount /home/usb/"
s1.expect
"#"
rmdir /home/usb/