平台化管理Linux环境的客户端程序的部署、更新、以及进程关闭
平台是用SSH实现,前端是avalon,数据库mysql客户端程序(Monitor, CLientSetup)是spring boot实现平台操作界面如下:

客户端程序访问路径为 http://ip:8034,
获取版本方式为 http://ip:8034/Deploy/Version
心跳地址为 http://ip:8034/Deploy/ReturnOk
1. 更新状态访问版本地址,返回该ip对应的版本,落入数据库,更新页面信息
2. 部署以及更新应用所有服务器路径都在ftpuser用户目录下,即/STRESS
应用源码放在服务器A上,为Monitor.tar,上传脚本Deploy.sh也在服务器A上,

上传脚本如下:
IP=$1
echo "put tar to "${IP}
ftp -n<<!
open ${IP}
user ftpuser 1qaz@WSX
binary
prompt
mkdir tools
cd tools
put Monitor.tar
put ClientSetup.tar
put clientDeploy.sh
#chmod 777 clientDeploy.sh
close
bye
!
平台调用上传脚本把源码从服务器A传到目标服务器B上。
平台再调用服务器B上脚本tools/clientDeploy.sh
cd tools
rm -rf Monitor
rm -rf ClientSetup
tar -xvf Monitor.tar
tar -xvf ClientSetup.tar
cd ~/tools/Monitor
sh restart.sh
cd ~/tools/ClientSetup
sh restart.sh
cd ~/tools
rm -rf Monitor.tar
rm -rf ClientSetup.tar
平台调用java代码
public String DeployEnvAgency(String ip) throws IOException, InterruptedException
{
RemoteShellTool tool = new RemoteShellTool("172.16.103.126", "ftpuser",
"******", "utf-8");
String result = tool.exec("sh Deploy.sh "+ip);
System.out.print("result:"+result);
RemoteShellTool tool2 = new RemoteShellTool(ip, "ftpuser",
"******", "utf-8");
String result2 = tool2.exec("sh tools/clientDeploy.sh");
System.out.print("result2:"+result2);
return "ok";
}
调用RemoteShellTool类如下:
package com.ymt.testplatform.util;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
public class RemoteShellTool {
private Connection conn;
private String ipAddr;
private String charset = Charset.defaultCharset().toString();
private String userName;
private String password;
public RemoteShellTool(String ipAddr, String charset) {
this.ipAddr = ipAddr;
if (charset != null) {
this.charset = charset;
}
}
public RemoteShellTool(String ipAddr, String userName, String password,
String charset) {
this.ipAddr = ipAddr;
this.userName = userName;
this.password = password;
if (charset != null) {
this.charset = charset;
}
}
public boolean login() throws IOException {
conn = new Connection(ipAddr);
conn.connect(); // 连接
return conn.authenticateWithPassword(userName, password); // 认证
}
public boolean loginCentos() throws IOException
{
conn = new Connection(ipAddr);
conn.connect(); // 连接
String [] pass = {"*****","*****","******","******","******"};
for (String pa : pass) {
if(conn.authenticateWithPassword("root", pa))
{
this.userName = "root";
this.password = pa;
return true;
}
}
return false;
}
public boolean login(String userName,String password) throws IOException {
conn = new Connection(ipAddr);
conn.connect(); // 连接
this.userName = userName;
this.password = password;
return conn.authenticateWithPassword(userName, password); // 认证
}
public String exec(String cmds) {
InputStream in = null;
String result = "";
try {
if (this.login()) {
Session session = conn.openSession(); // 打开一个会话
session.execCommand(cmds);
in = session.getStdout();
result = this.processStdout(in, this.charset);
session.close();
conn.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
return result;
}
public String processStdout(InputStream in, String charset) {
byte[] buf = new byte[1024];
StringBuffer sb = new StringBuffer();
try {
while (in.read(buf) != -1) {
sb.append(new String(buf, charset));
}
} catch (IOException e) {
e.printStackTrace();
}
return sb.toString();
}
/**
* @param args
*/
public static void main(String[] args) {
RemoteShellTool tool = new RemoteShellTool("172.16.103.126", "ftpuser",
"*****", "utf-8");
String result = tool.exec("sh Deploy.sh 172.16.103.121");
System.out.print("result:"+result);
RemoteShellTool tool2 = new RemoteShellTool("172.16.103.121", "ftpuser",
"*****", "utf-8");
String result2 = tool2.exec("sh tools/clientDeploy.sh");
System.out.print("result2:"+result2);
}
}
3. 关闭进程
java代码:
public String killClient(String ip) throws IOException, InterruptedException
{
RemoteShellTool tool = new RemoteShellTool(ip,"utf-8");
tool.loginCentos();
tool.exec("kill -9 $(ps -ef|grep ClientSetup|gawk '$0 !~/grep/ {print $2}'|tr -s '\n''')");
tool.exec("kill -9 $(ps -ef|grep SpringMVCDemo|gawk '$0 !~/grep/ {print $2}'|tr -s '\n''')");
return "ok";
}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="/css/reset.css" rel="stylesheet">
<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/admin/layout.css" rel="stylesheet">
<link href="/css/admin/envinfo.css" rel="stylesheet">
<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/bootstrap/js/jquery.bootpag.min.js"></script>
<script type="text/javascript" src="/lib/avalon.js"></script>
<script type="text/javascript" src="/js/common/util.js"></script>
<script type="text/javascript" src="/js/admin/common/common.js"></script>
<script type="text/javascript" src="/js/admin/monitordeploy.js"></script>
<title>基础信息管理</title>
</head>
<body ms-controller="vm">
<!-- ADMIN HEAD -->
<div ms-include-src="'/admin/header.html'"></div>
<!-- Content -->
<div class="container">
<div ms-controller="monitordeploy">
<div class="tabbable">
<div class="tab-content">
<div class="tab-pane active" id="vms">
<div id="vmsTab-pane">
<br/>
<div class="row" id="search1Div">
<div class="col-md-2">
<div class="input-group">
<span class="input-group-addon">环境:</span>
<select class="form-control" ms-duplex="envType">
<option value="STRESS" selected>STRESS</option>
<option value="ALL">全部</option>
<option value="SIT1">SIT1</option>
<option value="SIT2">SIT2</option>
<option value="UAT">UAT</option>
<option value="other">其他</option>
</select>
</div>
</div>
<div class="row" id="search2Div">
<div class="col-md-2">
<div class="input-group">
<span class="input-group-addon">类型:</span>
<select class="form-control" ms-duplex="conType">
<option value="" selected>请选择</option>
<option value=".Net Web">.Net Web</option>
<option value="Windows Service">Windows Service</option>
<option value="Node">Node</option>
<option value="Java App">Java App</option>
<option value="Java Web">Java Web</option>
<option value="其他">其他</option>
</select>
</div>
</div>
<div class="col-md-1">
<button type="button" id="searchBtn" class="btn btn-primary" ms-click="listVmInfosByPage('init')" style="margin: auto;">
搜 索
</button>
</div>
<div class="col-md-1">
<button type="button" id="allCheckBtn" class="btn btn-info" ms-click="checkAll()" style="margin: auto;">
全选
</button>
</div>
<div class="col-md-1">
<button type="button" id="allUncheckBtn" class="btn btn-info" ms-click="uncheckAll()" style="margin: auto;">
全不选
</button>
</div>
<div class="col-md-1">
<button type="button" id="allStatusBtn" class="btn btn-success" ms-click="viewAllStatus()" style="margin: auto;">
update all
</button>
</div>
<div class="col-md-1">
<button type="button" id="allDeployBtn" class="btn btn-warning" ms-click="deployAll()" style="margin: auto;">
deploy all
</button>
</div>
<div class="col-md-1">
<button type="button" id="allDeployBtn" class="btn btn-danger" ms-click="killAll()" style="margin: auto;">
kill all
</button>
</div>
</div>
<div>
<div id="pageSizeSelect"><a><span ms-class="{{pagesize1Cls}}"
ms-click="changePageSize(pagesize1)">{{pagesize1}}</span></a> | <a><span
ms-class="{{pagesize2Cls}}" ms-click="changePageSize(pagesize2)">{{pagesize2}}</span></a> |
<a>
<spam ms-class="{{pagesize3Cls}}" ms-click="changePageSize(pagesize3)">{{pagesize3}}</spam>
</a>
</div>
</div>
<table class="table table-condensed table-hover">
<thead>
<tr>
<td class="width-50"></td>
<td class="width-50">ID</td>
<td>名称</td>
<td class="width-125">IP</td>
<td class="width-300">操作系统</td>
<td class="width-100">监控版本</td>
<td class="width-100">Setup版本</td>
<td class="width-100">监控状态</td>
<td class="width-100">Setup状态</td>
<td class="width-100">更新时间</td>
<td class="width-50">状态更新</td>
<td class="width-50">部署</td>
<td class="width-50">kill</td>
</tr>
</thead>
<tbody>
<tr ms-repeat="vmsList">
<td><label><input type="checkbox" ms-class="check_{{el.name}}" ></label></td>
<td>{{$index+jpageSize*(jpageIndex-1)+1}}</td>
<td>{{el.name}}</td>
<td><a ms-href="'/admin/vmdetails.html?vmid='+el.vm.id" target="_blank">{{el.ip}}</a></td>
<td>{{el.os}}</td>
<td>{{el.version}}</td>
<td>{{el.setupVersion}}</td>
<td>{{el.clientOn}}</td>
<td>{{el.setupOn}}</td>
<td>{{el.time}}</td>
<td>
<div ms-class="buttonDiv_view_{{el.name}}">
<i style="color:#009100;" ms-class="glyphicon glyphicon-eye-open icon-white i_view_{{el.name}}" ms-click="postViewStatus(el.name,el.ClientOn,el.SetupOn,el.ip)"></i>
</div>
<div ms-class="loadDiv_view_{{el.name}} loadDiv" style="display:none">
<img src="/img/load2.jpg" style="width:16px;height:16px;"/>
</div>
</td>
<td>
<div ms-class="buttonDiv_{{el.name}}">
<i style="color:#000066;" ms-class="glyphicon glyphicon-play icon-white i_{{el.name}}" ms-click="postDeploy(el.name,el.ip,el.os)"> </i>
</div>
<div ms-class="loadDiv_{{el.name}} loadDiv" style="display:none">
<img src="/img/load2.jpg" style="width:16px;height:16px;"/>
</div>
</td>
<td>
<div ms-class="buttonDiv_kill_{{el.name}}">
<i style="color:#CE0000;" ms-class="glyphicon glyphicon-remove icon-white ikill_{{el.name}}" ms-click="postkill(el.name,el.ip,el.os)"> </i>
</div>
<div ms-class="loadDiv_kill_{{el.name}} loadDiv" style="display:none">
<img src="/img/load2.jpg" style="width:16px;height:16px;"/>
</div>
</td>
</tr>
</tbody>
</table>
<div class="text-center">
<p id="pagination"></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /.container-->
</body>
</html>
/**
* Created by chenjiazhu on 2017/2/15.
*/
var monitordeploy = avalon.define({
$id: 'monitordeploy',
//VM Start
pagesize1: "20",
pagesize1Cls: "pageSizeSelected",
pagesize2: "50",
pagesize2Cls: "",
pagesize3: "100",
pagesize3Cls: "",
changePageSize: function(pgsize) {
monitordeploy.jpageSize = pgsize;
monitordeploy.listVmInfosByPage("init");
},
clearsearch: function() {
monitordeploy.conType = "";
monitordeploy.listVmInfosByPage("init");
},
jpageIndex: 1,
jpageSize: 20,
envType: "STRESS",
conType: "",
vmsList: [],
listVmInfosByPage: function(tag) {
if (tag) {
monitordeploy.jpageIndex = 1;
}
$.ajax({
type: "post",
url: 'listDeployVmInfosByPageByEnvType.action',
data: {
"pageindex": monitordeploy.jpageIndex,
"pagesize": monitordeploy.jpageSize,
"type": monitordeploy.conType,
"envType": monitordeploy.envType
},
dataType: "json",
success: function(data) {
if (tag) {
$('#pagination').bootpag({
total: data.pagenum,
page: monitordeploy.jpageIndex
});
}
if (data.retCode == "1000") {
monitordeploy.vmsList = data.vms;
} else {
alert(data.retMSG);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("请求数据异常,状态码:" + XMLHttpRequest.status+",Error:"+errorThrown+",textStatus:"+textStatus);
}
});
},
postViewStatus: function(name, status1,status2,ip) {
if(status1==null || status1=="")
{
status1=0;
}
if(status2==null || status2=="")
{
status2=0;
}
$(".loadDiv_view_"+name).show();
$(".buttonDiv_view_"+name).hide();
// 检查Monitor状态
$.ajax({
type: "GET",
dataType : 'jsonp',
jsonp:"jsoncallback",
url: "http://"+ip+":8034/Deploy/ReturnOk",
success: function (data) {
if(data.data=="ok")
{
if(status1!="ok")
{
monitordeploy.updateMonitorStatus(ip,"ok");
}
}
else
{
if(status1!="fail")
{
monitordeploy.updateMonitorStatus(ip,"fail");
}
}
$(".loadDiv_view_"+name).hide();
$(".buttonDiv_view_"+name).show();
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("请求数据异常,状态码:" + XMLHttpRequest.status+",Error:"+errorThrown+",textStatus:"+textStatus);
$(".loadDiv_view_"+name).hide();
$(".buttonDiv_view_"+name).show();
}
});
// 检查ClientSetup状态
$.ajax({
type: "GET",
dataType : 'jsonp',
jsonp:"jsoncallback",
url: "http://"+ip+":8035/Deploy/ReturnOk",
success: function (data) {
if(data.data="ok")
{
if(status1!="ok")
{
monitordeploy.updateClientSetupStatus(ip,"ok");
}
}
else
{
if(status1!="fail")
{
monitordeploy.updateClientSetupStatus(ip,"fail");
}
}
$(".loadDiv_view_"+name).hide();
$(".buttonDiv_view_"+name).show();
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("请求数据异常,状态码:" + XMLHttpRequest.status+",Error:"+errorThrown+",textStatus:"+textStatus);
$(".loadDiv_view_"+name).hide();
$(".buttonDiv_view_"+name).show();
}
});
},
updateMonitorStatus: function(ip,status) {
$.ajax({
type: "post",
url: 'updateMonitorStatus.action',
data: {
"ip": ip,
"status1": status,
},
dataType: "json",
success: function(data) {
if (data.retCode != "1000") {
alert("更新"+ip+"Monitor状态失败!");
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("请求数据异常,状态码:" + XMLHttpRequest.status+ ",<br/>"+XMLHttpRequest.readyState+ ",<br/>"+XMLHttpRequest.responseText+",<br/>Error:"+errorThrown+",<br/>textStatus:"+textStatus);
}
});
},
updateClientSetupStatus: function(ip,status) {
$.ajax({
type: "post",
url: 'updateClientSetupStatus.action',
data: {
"ip": ip,
"status2": status,
},
dataType: "json",
success: function(data) {
if (data.retCode != "1000") {
alert("更新"+ip+"ClientSetupStatus!");
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert("请求数据异常,状态码:" + XMLHttpRequest.status+ ",<br/>"+XMLHttpRequest.readyState+ ",<br/>"+XMLHttpRequest.responseText+",<br/>Error:"+errorThrown+",<br/>textStatus:"+textStatus);
}
});
},
postDeploy: function(name,ip,os) {
$(".loadDiv_"+name).show();
$(".buttonDiv_"+name).hide();
$.ajax({
type: "post",
url: 'deploy.action',
data: {
"ip": ip,
"os": os
},
dataType: "json",
success: function(data) {
$(".loadDiv_"+name).hide();
$(".buttonDiv_"+name).show();
if(data.data=="false")
{
alert("更新"+ip+"客户端失败!");
}
else
{
monitordeploy.listVmInfosByPage();
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
$(".loadDiv_"+name).hide();
$(".buttonDiv_"+name).show();
alert("请求数据异常,状态码:" + XMLHttpRequest.status+ ",<br/>"+XMLHttpRequest.readyState+ ",<br/>"+XMLHttpRequest.responseText+",<br/>Error:"+errorThrown+",<br/>textStatus:"+textStatus);
}
});
},
postkill: function(name,ip,os) {
$(".loadDiv_kill_"+name).show();
$(".buttonDiv_kill_"+name).hide();
$.ajax({
type: "post",
url: 'kill.action',
data: {
"ip": ip,
"os": os
},
dataType: "json",
success: function(data) {
$(".loadDiv_kill_"+name).hide();
$(".buttonDiv_kill_"+name).show();
if(data.data=="false")
{
alert("杀死"+ip+"监控进程失败!");
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
$(".loadDiv_kill_"+name).hide();
$(".buttonDiv_kill_"+name).show();
alert("请求数据异常,状态码:" + XMLHttpRequest.status+ ",<br/>"+XMLHttpRequest.readyState+ ",<br/>"+XMLHttpRequest.responseText+",<br/>Error:"+errorThrown+",<br/>textStatus:"+textStatus);
}
});
},
viewAllStatus:function() {
$("input[type='checkbox']").each(
function(){
if($(this).get(0).checked)
{
var name = $(this).attr("class").substr(6);
//alert(name);
$(".i_view_"+name).click();
}
}
);
},
deployAll:function() {
$("input[type='checkbox']").each(
function(){
if($(this).get(0).checked)
{
var name = $(this).attr("class").substr(6);
//alert(name);
$(".i_"+name).click();
}
}
);
},
killAll:function() {
$("input[type='checkbox']").each(
function(){
if($(this).get(0).checked)
{
var name = $(this).attr("class").substr(6);
//alert(name);
$(".ikill_"+name).click();
}
}
);
},
checkAll:function(){
$("input[type='checkbox']").each(
function(){
$(this).attr("checked","true");
}
);
},
uncheckAll:function(){
$("input[type='checkbox']").each(
function(){
$(this).removeAttr("checked");
}
);
},
loadVmTAB: function() {
monitordeploy.listVmInfosByPage("init");
$('#vms').tab('show');
},
//VM END
userOps: ops(4),
bootpagFuc: function() {
$('#pagination').bootpag({
total: 1,
maxVisible: 10
}).on('page', function(event, num) {
monitordeploy.jpageIndex = num;
monitordeploy.listVmInfosByPage();
});
}
});
avalon.ready(function() {
if (monitordeploy.userOps) {
monitordeploy.loadVmTAB();
} else {
redirectAdminIndexPage();
}
monitordeploy.bootpagFuc();
// $(".loadDiv").hide();
});
monitordeploy.$watch("jpageSize", function(newValue) {
monitordeploy.pagesize1Cls = "";
monitordeploy.pagesize2Cls = "";
monitordeploy.pagesize3Cls = "";
if (newValue == monitordeploy.pagesize1) {
monitordeploy.pagesize1Cls = "pageSizeSelected";
} else if (newValue == monitordeploy.pagesize2) {
monitordeploy.pagesize2Cls = "pageSizeSelected";
} else if (newValue == monitordeploy.pagesize3) {
monitordeploy.pagesize3Cls = "pageSizeSelected";
}
})