平台化管理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 ' ''')"); tool.exec("kill -9 $(ps -ef|grep SpringMVCDemo|gawk '$0 !~/grep/ {print $2}'|tr -s ' ''')"); 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="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="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="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"; } })