linux Web控制檯

前段時間做一個hadoop+Spark的性能監控頁面時,需要一個web控制檯遠程登陸到master節點上去,後來發現這方面資料太少,於是自己參照着零散的東西修修改改,終於做出了一個簡單的web shell,記錄一下以免時間長了忘記。大概像這個樣子的:


這樣就可以在網頁上直接訪問linux服務器了,初衷是用來遠程關閉正在運行的spark任務的,做發現出來之後一般的linux命令都能執行。

首先講一下後臺實現:

1.建立ssh連接,並定義一些流用於收發命令。

2.其次定義是一個接收命令和返回結果的方法,因爲linux每次返回一行,所以我這裏存入List<String>返回給前臺處理。

代碼都如下:
package com.java.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;


public class SSHLinux
{
	private String hostname = "11.11.11.15";
	private int port = 22;
	private String username = "root";
	private String password = "123456";
	
	private  Connection conn;
	private  Session sess;
	private  BufferedReader stdout;
	private BufferedReader stderr;
	private PrintWriter pw;
	
	public SSHLinux()
	{
		//使用默認值		
		long t1 = System.currentTimeMillis();
		initSession();
		long t2 = System.currentTimeMillis();
		System.out.println("遠程登陸linux,連接耗時:"+(t2-t1)/1000.0+"s");
	}
	
	public SSHLinux(String hostname, int port, String username, String password) 
	{		
		this.hostname = hostname;
		this.port = port;
		this.username = username;
		this.password = password;
	
		long t1 = System.currentTimeMillis();
		initSession();
		long t2 = System.currentTimeMillis();
		System.out.println("遠程登陸linux,連接耗時:"+(t2-t1)/1000.0+"s");
	}
	
	//初始化連接並建立虛擬終端
	public void initSession()
	{
		try
		{			
			conn = new Connection(hostname,port);
			conn.connect();
			boolean isAuthenticated = conn.authenticateWithPassword(username, password);
			if (isAuthenticated == false)
				throw new IOException("Authentication failed.");
			
			sess = conn.openSession();
			sess.requestDumbPTY();//建立虛擬終端
			sess.startShell();//打開一個Shell	
			
			stdout = new BufferedReader(new InputStreamReader(new StreamGobbler(sess.getStdout()), StandardCharsets.UTF_8));
			stderr = new BufferedReader(new InputStreamReader(new StreamGobbler(sess.getStderr()), StandardCharsets.UTF_8));
			pw = new PrintWriter(sess.getStdin(),true);// 準備輸入命令
			
		}
		catch (IOException e)
		{
			System.out.println("------建立ssh linux連接錯誤------");
		}
	}
	
	
	public void close()
	{
		try
		{
			stdout.close();
			stderr.close();
			pw.close();
			sess.close();
			conn.close();
		}
		catch (Exception e) 
		{
			System.out.println("------關閉ssh連接錯誤------");
		}
	
	}

	public List<String> execute(String strcmd)
	{			
		System.out.println("在execute()中接收到命令:"+ strcmd );
		List<String> rstList = new ArrayList<String>();
		try
		{		
			if(strcmd.equals("exit"))
			{
				pw.println(strcmd);// 輸入待執行命令	
				close();
				rstList.add("用戶退出,關閉連接!!!");
				return rstList;
			}
			
			pw.println(strcmd);// 輸入待執行命令					
			
			while (true)
			{
				String line = stdout.readLine();
				if (line == null || line.trim().endsWith("#"))
					break;
				
				System.out.println(line);
				rstList.add(line);
			}
						
		}
		catch (IOException e)
		{
			System.out.println("------連接已經關閉或命令出錯------");
		}		
			
		return rstList;		
	}

	
	public static void main(String[] args)
	{
		SSHLinux ssh = new SSHLinux("192.168.0.160",22,"root","123456");
		List<String> list = ssh.execute("pwd");
		System.out.println("list----: "+list);
		//ssh.execute("exit");
	}
}


3.頁面後臺調用前面初始化方法和執行命令的方法,這個方法叫getLinux(),主要是和前臺頁面交互。我在這個方法命名爲linux,@RequestMapping("/linux"),前臺就可以通過linux.do識別了。

首先創建彈出框並初始化:

        /*
	 * 創建按鈕彈出框
	 */
	@RequestMapping("/addWindow")
	public ModelAndView goNewAddServer(HttpServletRequest req)
	{ 
		//ssh連接linux---------從xml配置文件中讀取參數
		ApplicationContext ct = new ClassPathXmlApplicationContext("applicationContext.xml");
		Permission per = (Permission) ct.getBean("linux");		
		String hostname = per.getIp();
		int port  = per.getPort();
		String username = per.getUsername();
		String password = per.getPassword();
		System.out.println("ssh連接linux---------Permission="+per.toString());
		
		try {
			ssh = new SSHLinux(hostname,port,username,password);
                        //或者將賬號寫死
			//ssh = new SSHLinux("192.168.1.12",22,"root","123456");
		} catch (Exception e) {			
			e.printStackTrace();
		}		
		return new ModelAndView("/admin/cloudcompute/box");///對應web console頁面
	}
其次執行命令,主要是和前臺頁面交互
        /*
	 * 在linux web shell中執行命令
	 */
	@RequestMapping("/linux")
	@ResponseBody
	private void getLinux(HttpServletRequest request, HttpServletResponse response)throws Exception{
		System.out.println("\n-----------------Linux Shell-----------------\n");		
		String cmd = request.getParameter("code");
		System.out.println("執行命令: "+cmd);
		
		if(ssh!=null)
		{
			List<String> list = new ArrayList<String>();
			if(cmd.length()==0)
			{
				System.out.println("輸入爲空");
				//list.add("輸入爲空");
			}
			else if(cmd!="" || cmd.length()!=0 || cmd!=null)
			{
				list = ssh.execute(cmd);	
			}
			
			System.out.println("返回結果list----: "+list);
			
			String result = ""; //{'data:','
			for(String str : list)
			{
				result += str.trim()+"<br/>";
			}
			//result +="'}";				
			ResponseUtil.write(response, result);
			//JsonBinder buildNormalBinder = JsonBinder.buildNormalBinder();
			//return buildNormalBinder.toJson(result);
		}
		else
		{
			System.out.println("沒有連接,不執行命令");
			//return "";
		}
						
	}//


4.接下來看頁面,頁面很簡單:主要是定義<ul>列表來接收命令,因爲發送的命令佔一行,回來的結果爲一行或多行,所以<ul>列表很合適。

<!-- 按鈕觸發模態框 -->
<span class="annnys">
	<a id="linuxWindow" class="pve-tabletack active-tabletack" href="javascript:establishDialog()" style="cursor:pointer;">控制檯</a>											
</span>
<!-- 模態框(Modal) -->
<div class="modal fade" id="myModal"
	 tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
	<div class="modal-dialog"  style="width:703px;height:410px;">
		<div class="modal-content">
			<div class="modal-header">
				<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
				<p class="modal-title" id="myModalLabel">Linux 控制檯</p>
			</div>
			<div class="modal-body" style="padding:0px;">
				<!-- <iframe id="box" src="box.jsp" style="width:600px;height:400px;"></iframe> -->
				
				<div class="wingb" id="msg">
					<ul class="myul">
					<li>----------------------命令Demo----------------------------</li>
					<li>關閉進程方式1:</li>	
					<li>ps -aux | grep test.jar		//根據提交的jar包名字來查找進程,再kill -9 [pid]殺掉進程</li>
					<li>關閉進程方式2:</li>	
					<li>ps -aux | grep test.jar | kill -9 `awk '{print $2}'`	//查找進程id並立即殺掉</li>
					<li>關閉進程方式3:</li>
					<li>kill -9 $(cat my.pid)	//前提:提交任務時獲取進程號: spark-submit --class demo.WordCount test.jar & echo $!>my.pid</li>
					<li>----------------------開始操作----------------------------</li>
					</ul>								
					<!-- <div class="incmd"  contentEditable="true"  id='in'>$</div> -->
					<input class="incmd" type="text" value="$" id='in'>
				</div>
				
			</div><!-- modal-body -->
		</div><!-- /.modal-content -->
	</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<script>				
$("#in").keyup(function(event) {					
	if (event.keyCode == 13) {	//回車			
		$.ajax({
			async: false,
			type : "POST",
			url : "${pageContext.request.contextPath}/admin/cloudcompute/linux.do",
			data : "code=" + $("#in").val().substring(1),
			success : function(data) {
				$("ul").append("<li>" + $("#in").val() + "</li>");  //將輸入的輸出到界面
				$("ul").append("<li>" + data + "</li>"); //獲取返回值並輸出
				$("#in").val("$"); //清空輸入框
				$("#msg").scrollTop($("#msg").scrollTop() + 9999);//滾動條拉到最下面,顯示出輸入框
			}
		});
	}
	else  if(event.ctrlKey && event.which == 81){ //ctrl+Q 中斷
		alert("ctrl+Q 中斷");
		 CloseWebPage();
	}
});

$("#in")[0].focus();

function CloseWebPage() {
	if (navigator.userAgent.indexOf("MSIE") > 0) {
		if (navigator.userAgent.indexOf("MSIE 6.0") > 0) {
			window.opener = null;
			window.close();
		} else {
			window.open('', '_top');
			window.top.close();
		}
	} else if (navigator.userAgent.indexOf("Firefox") > 0) {
		window.location.href = 'about:blank ';
	} else {
		window.opener = null;
		window.open('', '_self', '');
		window.close();
	}
}

//創建按鈕 彈出框
function establishDialog(){
	xajax.iframeLAYER('${pageContext.request.contextPath}/admin/cloudcompute/addWindow.do',
			"linux控制檯", 
			'620px', 
			'430px',
			function(){	
	});
		
}

</script>

這是原來的頁面中增加的一個模態框,通過按鈕觸發。當然也可以新建一個頁面專門來做控制檯,代碼都一樣,如圖:

<%@ page language="java" contentType="text/html; charset=UTF-8"  pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>命令行窗口</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/public-js/jquery-1.8.0.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/static/layer/layer.js"></script>

<style type="text/css">
body{
	background-color: #424242;  /*背景顏色*/
	font-size:14px;
	font: "微軟雅黑";
}


.incmd {
	background-color: #9FB6CD; /*輸入行顏色*/
	border: 0;
	color: #FFFFFF; /*輸入字體顏色*/
	outline: none;
	font-size:14px; 
	width: 99%;
}

.panel {/*暫時未用*/
	background-color: #424242;  /*背景顏色*/
	border-top: #424242 outset 2px;/*上邊框*/
 	width: 700px;
	height: 500px;
	overflow-y: scroll;
	overflow-x:visible;
	font-size:14px;
	
}

ul {
	margin: 0px;
	padding: 0px;
	list-style: none;
	color:#7CFC00; /*顯示字體顏色*/
}

input {
	background-color: #9FB6CD; /*輸入行顏色*/
	border: 0;
	color: #FFFFFF; /*輸入字體顏色*/
	outline: none;
	font-size:14px; 
	width: 99%;
}
</style>

<script>
$(function(){
	$("#in").keyup(function(event) {		
	
		if (event.keyCode == 13) {	//回車			
			$.ajax({
				async: false,
				type : "POST",
				url : "${pageContext.request.contextPath}/admin/cloudcompute/linux.do",
				data : "code=" + $("#in").val().substring(1),
				success : function(data) {
					$("ul").append("<li>" + $("#in").val() + "</li>");  //將輸入的輸出到界面
					$("ul").append("<li>" + data + "</li>"); //獲取返回值並輸出
					$("#in").val("$"); //清空輸入框
					$("#msg").scrollTop($("#msg").scrollTop() + 32);//滾動條拉到最下面,顯示出輸入框
				}
			});
		}
		/* else  if(event.ctrlKey && event.which == 81){ //ctrl+Q 中斷
        	alert("ctrl+Q 中斷");
        }
		else  if(event.which == 27){ //ESC 退出			
        	alert("ESC終端");
        	CloseWebPage();
        }
			 */
	});

	$("#in")[0].focus();	
	
});
	
	

function CloseWebPage() {
	if (navigator.userAgent.indexOf("MSIE") > 0) {
		if (navigator.userAgent.indexOf("MSIE 6.0") > 0) {
			window.opener = null;
			window.close();
		} else {
			window.open('', '_top');
			window.top.close();
		}
	} else if (navigator.userAgent.indexOf("Firefox") > 0) {
		window.location.href = 'about:blank ';
	} else {
		window.opener = null;
		window.open('', '_self', '');
		window.close();
	}
}
</script>
</head>

<body>
<div id="msg">
	<ul>
	<li>----------------------命令Demo----------------------------</li>
	<li>關閉進程方式1:</li>	
	<li>ps -aux | grep test.jar		//根據提交的jar包名字來查找進程,再kill -9 [pid]殺掉進程</li>
	<li>關閉進程方式2:</li>	
	<li>ps -aux | grep test.jar | kill -9 `awk '{print $2}'`	//查找進程id並立即殺掉</li>
	<li>關閉進程方式3:</li>
	<li>kill -9 $(cat my.pid)	//前提:提交任務時獲取進程號: spark-submit --class demo.WordCount test.jar & echo $!>my.pid</li>
	<li>----------------------開始操作----------------------------</li>
	</ul>

	<!-- <div class="incmd"  contentEditable="true"  id='in'>$</div> -->
	<input type="text" value="$" id='in'>
</div>

</body>
</html>

5.頁面調用後臺代碼的部分都是一樣的,linux.do就會調轉到前面提到的getLinux()方法。

ok, 本文主要從後臺到前臺的順序講了如何實現一個簡單web shell。

完。

我準備寫一個公衆號技術博客,回顧我學大數據以來的個人經驗,希望和大家一起每天進步一點點!剛剛開始寫,請大家多多支持,如有不足之處多多包含,最後多多關注哈哈哈。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章