linux/unix/mac 下通过expect/tcl 脚本透过跳板机一键登录目标机器

公司登录日志服务器要求先登上一个中间服务器(即常说的跳板机), 再通过中间服务器登录到目标服务器。 

这个过程操作麻烦, 主要有如下问题:

  1.  登录中间服务器需要账号和密码
  2. 登录到中间服务器之后, 需要通过ssh方式登录目标服务器。
  3. 登录目标服务器之后需要cd到相应的日志目录下
整个过程比较复杂, 虽然中间服务器是确定的, 但是线上服务器往往有很多, 每次都自己去查询相应的服务器, 然后透过中间服务器登录也是很麻烦的, 所以, 通过使用expect/tcl的方式, 整体出自动登录到目标服务器的方式

整体思路是:

  1. 将目标服务器的树状结构写到配置文件中去
  2. 将中间服务器以及目标服务器的账号密码也写到配置文件中
  3. 登录服务器时输入相应的参数, 如项目名称, 模块名称, 机器序号等
首先需要解决的就是读取配置文件的问题, 由于bash下有很方便的awk和sed等命令, 在开始考虑过通过bash嵌套expect方式, 实际操作时, 发现登录到目标服务器之后无法停留, 自动退出, 退出等待时间或长或短。因此最终考虑全部使用expect/tcl实现

首先分享一个使用expect/tcl读取ini配置文件的wiki:  点击打开链接

接下来expect脚本代码

#!/usr/bin/expect
 namespace eval cfg {
    variable version 1.0

    variable sections [list DEFAULT]

    variable cursection DEFAULT
    variable DEFAULT;   # DEFAULT section
 }

 proc cfg::sections {} {
    return $cfg::sections
 }
 

 proc cfg::variables {{section DEFAULT}} {
    return [array names ::cfg::$section]
 }

 proc cfg::add_section {str} {
    variable sections
    variable cursection

    set cursection [string trim $str \[\]]
    if {[lsearch -exact $sections $cursection] == -1} {
      lappend sections $cursection
      variable ::cfg::${cursection}
    }
 }

 proc cfg::setvar {varname value {section DEFAULT}} {
    variable sections
    if {[lsearch -exact $sections $section] == -1} {
      cfg::add_section $section
    }
    set ::cfg::${section}($varname) $value
 }

 proc cfg::getvar {varname {section DEFAULT}} {
    variable sections
    if {[lsearch -exact $sections $section] == -1} {
      error "No such section: $section"
    }
   return [set ::cfg::${section}($varname)]
 }


 proc cfg::parse_file {filename} {
    variable sections
    variable cursection
    set line_no 1
    set fd [open $filename r]
    while {![eof $fd]} {
        set line [string trim [gets $fd] " "]
        if {$line == ""} continue
        switch -regexp -- $line {
           ^#.* { }
           ^\\[.*\\]$ {
               cfg::add_section $line
           }
           .*=.* {
               set pair [split $line =]
               set name [string trim [lindex $pair 0] " "]
               set value [string trim [lindex $pair 1] " "]
               cfg::setvar $name $value $cursection
           } 
           default {
               error "Error parsing $filename (line: $line_no): $line"
           }
        }
        incr line_no
     }
     close $fd
 }


 set projectType [lindex $argv 0]
 set moduleType  [lindex $argv 1]
 set hostIndex   [lindex $argv 2]

 set configPath  config/config.ini
 cfg::parse_file $configPath
 set jump_host   $cfg::jump_server(host)
 set jump_port   $cfg::jump_server(port)
 set jump_user   $cfg::jump_server(username)
 set jump_pwd    $cfg::jump_server(password)
 set log_user    $cfg::log_server(username)
 set log_pwd     $cfg::log_server(password)
 set log_port    $cfg::log_server(port)

 set configPath  config/$projectType.ini
 cfg::parse_file $configPath
 set projectName $cfg::main(project_name)
 set moduleName [cfg::getvar module_name $moduleType]
 set host       [cfg::getvar host_$hostIndex $moduleType]

 set time -1
 spawn ssh -p $jump_port $jump_user@$jump_host
 expect "$jump_user@$jump_host's password:" {
 	send "$jump_pwd\r"
 }
 expect "$jump_user:~\\\$" {
 	send "ssh -p $log_port $log_user@$host\r"
 }
 expect "$log_user@$host's password:" {
 	send "$log_pwd\r"
 }
 expect "$log_user:~\\\$" {
 	send "cd /var/log/$projectName/$moduleName/logs\r"
 }
 interact


proc cfg相关的函数都是从tcl脚本语言官方的config wiki上拷贝下来的, 具体使用详见上面的链接

需要说明的是, 如果section是固定的,可以使用$cfg::SECTIONNAME(KEYNAME)的方式

如果section是变量, 则需要使用 [cfg::getvar key section]方式调用,

另外, expect的具体使用方式网上可以搜到很多, 这里就不多说了


另外, 配个我这边的配置文件结构





第一张图是通用配置, 第二张图片是每个项目的配置, 一般可以考虑一个项目一个配置文件, 根据项目简称找到项目的配置文件, 读取相应的模块配置即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值