system monitor added
parent
371a696fce
commit
33d61b1805
46
app/pom.xml
46
app/pom.xml
|
@ -78,16 +78,16 @@
|
|||
<artifactId>jsch</artifactId>
|
||||
<version>0.1.54</version>
|
||||
</dependency>
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>net.java.dev.jna</groupId> -->
|
||||
<!-- <artifactId>jna-platform</artifactId> -->
|
||||
<!-- <version>5.3.1</version> -->
|
||||
<!-- </dependency> -->
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>net.java.dev.jna</groupId> -->
|
||||
<!-- <artifactId>jna</artifactId> -->
|
||||
<!-- <version>5.3.1</version> -->
|
||||
<!-- </dependency> -->
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna-platform</artifactId>
|
||||
<version>5.3.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>5.3.1</version>
|
||||
</dependency>
|
||||
<!-- <dependency> -->
|
||||
<!-- <groupId>log4j</groupId> -->
|
||||
<!-- <artifactId>log4j</artifactId> -->
|
||||
|
@ -128,6 +128,32 @@
|
|||
<version>1.21</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-dist</artifactId>
|
||||
<version>3.13.3</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-core</artifactId>
|
||||
<version>3.13.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-json</artifactId>
|
||||
<version>3.13.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.oshi</groupId>
|
||||
<artifactId>oshi-parent</artifactId>
|
||||
<version>3.13.3</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
|
|
|
@ -36,6 +36,9 @@ import cloudshell.app.files.PosixPermission;
|
|||
import cloudshell.app.files.copy.FileCopyProgressResponse;
|
||||
import cloudshell.app.files.copy.FileCopyRequest;
|
||||
import cloudshell.app.files.search.SearchResult;
|
||||
import cloudshell.app.health.ProcessInfo;
|
||||
import cloudshell.app.health.SystemHealthMonitor;
|
||||
import cloudshell.app.health.SystemStats;
|
||||
import cloudshell.app.terminal.PtySession;
|
||||
|
||||
/**
|
||||
|
@ -56,6 +59,9 @@ public class AppController {
|
|||
@Autowired
|
||||
private BCryptPasswordEncoder passwordEncoder;
|
||||
|
||||
@Autowired
|
||||
private SystemHealthMonitor healthMon;
|
||||
|
||||
@PostMapping("/app/terminal/{appId}/resize")
|
||||
public void resizePty(@PathVariable String appId,
|
||||
@RequestBody Map<String, Integer> body) {
|
||||
|
@ -314,4 +320,22 @@ public class AppController {
|
|||
Files.createFile(path);
|
||||
}
|
||||
|
||||
@GetMapping("/app/sys/stats")
|
||||
public SystemStats getStats() {
|
||||
return this.healthMon.getStats();
|
||||
}
|
||||
|
||||
@GetMapping("/app/sys/procs")
|
||||
public List<ProcessInfo> getProcessList() {
|
||||
return this.healthMon.getProcessList();
|
||||
}
|
||||
|
||||
@PostMapping("/app/sys/procs")
|
||||
public Map<String, Boolean> killProcesses(
|
||||
@RequestBody List<Integer> pidList) {
|
||||
Map<String, Boolean> map = new HashMap<>();
|
||||
map.put("success", this.healthMon.killProcess(pidList));
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ public class FileTypeDetector {
|
|||
return tika.detect(file);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package cloudshell.app.health;
|
||||
|
||||
/**
|
||||
* @author subhro
|
||||
*
|
||||
*/
|
||||
public class ProcessInfo {
|
||||
private String name, command, user, state;
|
||||
private int pid, priority;
|
||||
private double cpuUsage, memoryUsage, vmUsage;
|
||||
private long startTime;
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the command
|
||||
*/
|
||||
public String getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param command the command to set
|
||||
*/
|
||||
public void setCommand(String command) {
|
||||
this.command = command;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the user
|
||||
*/
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param user the user to set
|
||||
*/
|
||||
public void setUser(String user) {
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the state
|
||||
*/
|
||||
public String getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param state the state to set
|
||||
*/
|
||||
public void setState(String state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the pid
|
||||
*/
|
||||
public int getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param pid the pid to set
|
||||
*/
|
||||
public void setPid(int pid) {
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cpuUsage
|
||||
*/
|
||||
public double getCpuUsage() {
|
||||
return cpuUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cpuUsage the cpuUsage to set
|
||||
*/
|
||||
public void setCpuUsage(double cpuUsage) {
|
||||
this.cpuUsage = cpuUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the memoryUsage
|
||||
*/
|
||||
public double getMemoryUsage() {
|
||||
return memoryUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param memoryUsage the memoryUsage to set
|
||||
*/
|
||||
public void setMemoryUsage(double memoryUsage) {
|
||||
this.memoryUsage = memoryUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the vmUsage
|
||||
*/
|
||||
public double getVmUsage() {
|
||||
return vmUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param vmUsage the vmUsage to set
|
||||
*/
|
||||
public void setVmUsage(double vmUsage) {
|
||||
this.vmUsage = vmUsage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the startTime
|
||||
*/
|
||||
public long getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startTime the startTime to set
|
||||
*/
|
||||
public void setStartTime(long startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the priority
|
||||
*/
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param priority the priority to set
|
||||
*/
|
||||
public void setPriority(int priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package cloudshell.app.health;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import oshi.SystemInfo;
|
||||
import oshi.hardware.CentralProcessor;
|
||||
import oshi.hardware.GlobalMemory;
|
||||
import oshi.hardware.HardwareAbstractionLayer;
|
||||
import oshi.software.os.OSProcess;
|
||||
import oshi.software.os.OperatingSystem;
|
||||
|
||||
/**
|
||||
* @author subhro
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class SystemHealthMonitor {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private CentralProcessor processor;
|
||||
private GlobalMemory memory;
|
||||
private OperatingSystem os;
|
||||
private boolean isWindows;
|
||||
|
||||
private SystemInfo si;
|
||||
|
||||
public SystemHealthMonitor() {
|
||||
si = new SystemInfo();
|
||||
HardwareAbstractionLayer hal = si.getHardware();
|
||||
os = si.getOperatingSystem();
|
||||
processor = hal.getProcessor();
|
||||
memory = hal.getMemory();
|
||||
processor.getSystemCpuLoadBetweenTicks();
|
||||
isWindows = System.getProperty("os.name").toLowerCase()
|
||||
.contains("windows");
|
||||
}
|
||||
|
||||
public synchronized SystemStats getStats() {
|
||||
SystemStats stats = new SystemStats();
|
||||
double cpuUsed = processor.getSystemCpuLoadBetweenTicks() * 100;
|
||||
|
||||
stats.setCpuUsed(cpuUsed);
|
||||
stats.setCpuFree(100 - cpuUsed);
|
||||
|
||||
long avail = memory.getAvailable();
|
||||
long total = memory.getTotal();
|
||||
if (total > 0) {
|
||||
double memoryUsed = ((total - avail) * 100) / total;
|
||||
stats.setMemoryUsed(memoryUsed);
|
||||
stats.setMemoryFree(100 - memoryUsed);
|
||||
}
|
||||
|
||||
File f = new File("/");
|
||||
long totalDiskSpace = f.getTotalSpace();
|
||||
long freeDiskSpace = f.getFreeSpace();
|
||||
if (totalDiskSpace > 0) {
|
||||
double diskUsed = ((totalDiskSpace - freeDiskSpace) * 100)
|
||||
/ totalDiskSpace;
|
||||
stats.setDiskUsed(diskUsed);
|
||||
stats.setDiskFree(100 - diskUsed);
|
||||
}
|
||||
|
||||
long totalSwap = memory.getSwapTotal();
|
||||
long usedSwap = memory.getSwapUsed();
|
||||
if (totalSwap > 0) {
|
||||
double swapUsed = usedSwap * 100 / totalSwap;
|
||||
stats.setSwapUsed(swapUsed);
|
||||
stats.setSwapFree(100 - swapUsed);
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
public synchronized List<ProcessInfo> getProcessList() {
|
||||
OSProcess[] procs = os.getProcesses(0, null, false);
|
||||
List<ProcessInfo> list = new ArrayList<>();
|
||||
if (procs != null && procs.length > 0) {
|
||||
for (OSProcess proc : procs) {
|
||||
ProcessInfo info = new ProcessInfo();
|
||||
info.setPid(proc.getProcessID());
|
||||
info.setName(proc.getName());
|
||||
info.setCommand(proc.getCommandLine());
|
||||
info.setCpuUsage(proc.calculateCpuPercent());
|
||||
info.setMemoryUsage(proc.getResidentSetSize());
|
||||
info.setVmUsage(proc.getVirtualSize());
|
||||
info.setState(proc.getState().toString());
|
||||
info.setStartTime(proc.getStartTime());
|
||||
info.setUser(proc.getUser());
|
||||
info.setPriority(proc.getPriority());
|
||||
list.add(info);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private boolean killProcess(int pid) {
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
isWindows ? Arrays.asList("taskkill", "/pid", pid + "", "/f")
|
||||
: Arrays.asList("kill", "-9", pid + ""));
|
||||
try {
|
||||
Process proc = pb.start();
|
||||
int ret = proc.waitFor();
|
||||
return ret == 0;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean killProcess(List<Integer> pidList) {
|
||||
boolean success = true;
|
||||
for (Integer pid : pidList) {
|
||||
if (success) {
|
||||
success = killProcess(pid);
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package cloudshell.app.health;
|
||||
|
||||
/**
|
||||
* @author subhro
|
||||
*
|
||||
*/
|
||||
public class SystemStats {
|
||||
private double cpuUsed = -1, cpuFree = -1, memoryUsed = -1, memoryFree = -1,
|
||||
swapUsed = -1, swapFree = -1, diskUsed = -1, diskFree = -1;
|
||||
|
||||
/**
|
||||
* @return the cpuUsed
|
||||
*/
|
||||
public double getCpuUsed() {
|
||||
return cpuUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cpuUsed the cpuUsed to set
|
||||
*/
|
||||
public void setCpuUsed(double cpuUsed) {
|
||||
this.cpuUsed = cpuUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the cpuFree
|
||||
*/
|
||||
public double getCpuFree() {
|
||||
return cpuFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cpuFree the cpuFree to set
|
||||
*/
|
||||
public void setCpuFree(double cpuFree) {
|
||||
this.cpuFree = cpuFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the memoryUsed
|
||||
*/
|
||||
public double getMemoryUsed() {
|
||||
return memoryUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param memoryUsed the memoryUsed to set
|
||||
*/
|
||||
public void setMemoryUsed(double memoryUsed) {
|
||||
this.memoryUsed = memoryUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the memoryFree
|
||||
*/
|
||||
public double getMemoryFree() {
|
||||
return memoryFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param memoryFree the memoryFree to set
|
||||
*/
|
||||
public void setMemoryFree(double memoryFree) {
|
||||
this.memoryFree = memoryFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the swapUsed
|
||||
*/
|
||||
public double getSwapUsed() {
|
||||
return swapUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param swapUsed the swapUsed to set
|
||||
*/
|
||||
public void setSwapUsed(double swapUsed) {
|
||||
this.swapUsed = swapUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the swapFree
|
||||
*/
|
||||
public double getSwapFree() {
|
||||
return swapFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param swapFree the swapFree to set
|
||||
*/
|
||||
public void setSwapFree(double swapFree) {
|
||||
this.swapFree = swapFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the diskUsed
|
||||
*/
|
||||
public double getDiskUsed() {
|
||||
return diskUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param diskUsed the diskUsed to set
|
||||
*/
|
||||
public void setDiskUsed(double diskUsed) {
|
||||
this.diskUsed = diskUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the diskFree
|
||||
*/
|
||||
public double getDiskFree() {
|
||||
return diskFree;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param diskFree the diskFree to set
|
||||
*/
|
||||
public void setDiskFree(double diskFree) {
|
||||
this.diskFree = diskFree;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package cloudshell.app.terminal;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* @author subhro
|
||||
*
|
||||
*/
|
||||
public interface PtyProcessPipe {
|
||||
public int read(byte[] b) throws Exception;
|
||||
|
||||
public void write(byte[] b, int off, int len) throws Exception;
|
||||
|
||||
public void resizePty(int col, int row, int wp, int hp);
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/**
|
||||
*
|
||||
*/
|
||||
package cloudshell.app.terminal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.io.PipedReader;
|
||||
import java.io.PipedWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.jcraft.jsch.ChannelShell;
|
||||
import com.jcraft.jsch.JSch;
|
||||
import com.jcraft.jsch.Session;
|
||||
import com.jcraft.jsch.UserInfo;
|
||||
|
||||
/**
|
||||
* @author subhro
|
||||
*
|
||||
*/
|
||||
public class SshPtyProcessPipe implements PtyProcessPipe, UserInfo {
|
||||
|
||||
private PipedInputStream _in, in;
|
||||
private PipedOutputStream _out, out;
|
||||
private String hostName, keyFile, user;
|
||||
private int port;
|
||||
private JSch jsch;
|
||||
private Session session;
|
||||
private ChannelShell shell;
|
||||
|
||||
/**
|
||||
* @throws IOException
|
||||
* @throws UnsupportedEncodingException
|
||||
*
|
||||
*/
|
||||
public SshPtyProcessPipe() {
|
||||
hostName = "192.168.56.106";
|
||||
port = 22;
|
||||
user = "subhro";
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
_out = new PipedOutputStream();
|
||||
in = new PipedInputStream(_out, 1);
|
||||
_in = new PipedInputStream(1);
|
||||
out = new PipedOutputStream(_in);
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
_start();
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
public void _start() throws Exception {
|
||||
|
||||
jsch = new JSch();
|
||||
JSch.setConfig("MaxAuthTries", "5");
|
||||
|
||||
if (keyFile != null && keyFile.length() > 0) {
|
||||
jsch.addIdentity(keyFile);
|
||||
}
|
||||
|
||||
session = jsch.getSession(user, hostName, port);
|
||||
|
||||
session.setUserInfo(this);
|
||||
// session.setConfig("StrictHostKeyChecking", "no");
|
||||
session.setConfig("PreferredAuthentications",
|
||||
"publickey,keyboard-interactive,password");
|
||||
|
||||
System.out.println("Before connect");
|
||||
|
||||
session.connect();
|
||||
|
||||
System.out.println("Client version: " + session.getClientVersion());
|
||||
System.out.println("Server host: " + session.getHost());
|
||||
System.out.println("Server version: " + session.getServerVersion());
|
||||
System.out.println(
|
||||
"Hostkey: " + session.getHostKey().getFingerPrint(jsch));
|
||||
|
||||
shell = (ChannelShell) session.openChannel("shell");
|
||||
shell.setEnv("TERM", "xterm");
|
||||
|
||||
shell.setInputStream(in);
|
||||
shell.setOutputStream(out);
|
||||
|
||||
shell.connect();
|
||||
}
|
||||
|
||||
public void resizePty(int col, int row, int wp, int hp) {
|
||||
shell.setPtySize(col, row, wp, hp);
|
||||
}
|
||||
|
||||
private String readLine() throws IOException {
|
||||
System.out.println("Attempt readline");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (true) {
|
||||
int ch = in.read();
|
||||
System.out.println("char: " + ch);
|
||||
if (ch == -1 || ch == '\n'|| ch == '\r')
|
||||
break;
|
||||
sb.append((char) ch);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassphrase() {
|
||||
try {
|
||||
return readLine();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
try {
|
||||
String pass = readLine();
|
||||
System.out.println("Paww: " + pass);
|
||||
return pass;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptPassword(String message) {
|
||||
System.out.println("prompt password: " + message);
|
||||
try {
|
||||
out.write(message.getBytes("utf-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptPassphrase(String message) {
|
||||
try {
|
||||
out.write(message.getBytes("utf-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptYesNo(String message) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(String message) {
|
||||
System.out.println("prompt messae: " + message);
|
||||
try {
|
||||
out.write(message.getBytes("utf-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws Exception {
|
||||
return _in.read(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws Exception {
|
||||
this._out.write(b, off, len);
|
||||
}
|
||||
|
||||
}
|
|
@ -20,13 +20,16 @@
|
|||
"@angular/platform-browser-dynamic": "~7.2.0",
|
||||
"@angular/router": "~7.2.0",
|
||||
"@ng-bootstrap/ng-bootstrap": "^4.2.0",
|
||||
"@swimlane/ngx-charts": "^12.0.1",
|
||||
"bootstrap": "^4.3.1",
|
||||
"chart.js": "^2.8.0",
|
||||
"core-js": "^2.5.4",
|
||||
"font-awesome": "^4.7.0",
|
||||
"ng2-ace-editor": "^0.3.9",
|
||||
"ng2-charts": "^2.2.3",
|
||||
"rxjs": "~6.3.3",
|
||||
"tslib": "^1.9.0",
|
||||
"xterm": "^3.13.2",
|
||||
"xterm": "^3.14.5",
|
||||
"zone.js": "~0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations'
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import {NgxChartsModule} from '@swimlane/ngx-charts';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
@ -54,7 +57,10 @@ import { NewItemComponent } from './home/files/browser/new-item/new-item.compone
|
|||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
NgbModule,
|
||||
HttpClientModule
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
NgxChartsModule,
|
||||
ChartsModule
|
||||
],
|
||||
providers: [httpInterceptorProviders],
|
||||
bootstrap: [AppComponent]
|
||||
|
|
|
@ -78,7 +78,7 @@ export class DataService {
|
|||
|
||||
currentViewChanger = new Subject<string>();
|
||||
|
||||
viewTextRequests=new Subject<string>();
|
||||
viewTextRequests = new Subject<string>();
|
||||
|
||||
terminalSession: TerminalSession;
|
||||
|
||||
|
@ -404,4 +404,16 @@ export class DataService {
|
|||
return this.http.get<any>(environment.BASE_URL + "token/temp");
|
||||
}
|
||||
|
||||
getSystemStats(): Observable<any> {
|
||||
return this.http.get<any>(environment.BASE_URL + "app/sys/stats");
|
||||
}
|
||||
|
||||
getProcessList(): Observable<any[]> {
|
||||
return this.http.get<any[]>(environment.BASE_URL + "app/sys/procs");
|
||||
}
|
||||
|
||||
killProcesses(pids: any[]): Observable<any> {
|
||||
return this.http.post<any>(environment.BASE_URL + "app/sys/procs", pids);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,22 +20,31 @@
|
|||
Settings
|
||||
</a> -->
|
||||
|
||||
<span style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view=null" [ngClass]="{'active-link': view==null}">
|
||||
Files
|
||||
</span>
|
||||
<span style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view='editor'" [ngClass]="{'active-link': view=='editor'}">
|
||||
Editor
|
||||
</span>
|
||||
<span style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view='search'" [ngClass]="{'active-link': view=='search'}">
|
||||
Search
|
||||
</span>
|
||||
<span style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view='settings'" [ngClass]="{'active-link': view=='settings'}">
|
||||
Settings
|
||||
</span>
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view='sysmon'" [ngClass]="{'active-link': view=='sysmon'}">
|
||||
Monitoring
|
||||
</span>
|
||||
|
||||
<!-- <span *ngFor="let tab of tabs; let i=index"
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center;"
|
||||
|
@ -50,22 +59,31 @@
|
|||
<div style="flex: 1; line-height: 55px; color: gray; font-size: 22px;">
|
||||
<span><i class="fa fa-terminal" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
<span style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view=null" [ngClass]="{'active-link': view==null}">
|
||||
<i class="fa fa-folder"></i>
|
||||
</span>
|
||||
<span style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view='editor'" [ngClass]="{'active-link': view=='editor'}">
|
||||
<i class="fa fa-file"></i>
|
||||
</span>
|
||||
<span style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view='search'" [ngClass]="{'active-link': view=='search'}">
|
||||
<i class="fa fa-search"></i>
|
||||
</span>
|
||||
<span style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view='settings'" [ngClass]="{'active-link': view=='settings'}">
|
||||
<i class="fa fa-cogs"></i>
|
||||
</span>
|
||||
<span
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center; text-decoration: none;"
|
||||
(click)="view='sysmon'" [ngClass]="{'active-link': view=='sysmon'}">
|
||||
<i class="fa fa-bar-chart" aria-hidden="true"></i>
|
||||
</span>
|
||||
|
||||
<!-- <span *ngFor="let tab of tabs; let i=index"
|
||||
style="cursor: pointer; line-height: 55px; padding-left: 15px; padding-right: 15px; color: gray; text-align: center;"
|
||||
|
@ -76,11 +94,12 @@
|
|||
</div>
|
||||
|
||||
<div class="router-wrapper">
|
||||
<app-editor [style.display]="view=='editor'?'block':'none'" ></app-editor>
|
||||
<app-search [style.display]="view=='search'?'block':'none'" ></app-search>
|
||||
<app-settings [style.display]="view=='settings'?'block':'none'" ></app-settings>
|
||||
<div style="width: 100vw; height: calc(100vh - 55px);" [style.display]="view?'none':'block'" >
|
||||
<app-files (viewChanged)="view=$event"></app-files>
|
||||
<app-monitoring *ngIf="view=='sysmon'"></app-monitoring>
|
||||
<app-editor [style.display]="view=='editor'?'block':'none'"></app-editor>
|
||||
<app-search [style.display]="view=='search'?'block':'none'"></app-search>
|
||||
<app-settings [style.display]="view=='settings'?'block':'none'"></app-settings>
|
||||
<div style="width: 100vw; height: calc(100vh - 55px);" [style.display]="view?'none':'block'">
|
||||
<app-files (viewChanged)="view=$event"></app-files>
|
||||
</div>
|
||||
|
||||
<!-- <router-outlet></router-outlet> -->
|
||||
|
@ -130,14 +149,16 @@
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<div style="position: fixed; width: 100vw; height: 100vh; z-index: 300; left: 0px; top: 0px; display: flex; flex-direction: column; background: rgb(40,40,40);"
|
||||
<div
|
||||
style="position: fixed; width: 100vw; height: 100vh; z-index: 300; left: 0px; top: 0px; display: flex; flex-direction: column; background: rgb(40,40,40);"
|
||||
*ngIf="mobileTerminalVisible">
|
||||
<div style="display: flex; justify-content: space-between; width: 100%; height: 40px; line-height: 40px;">
|
||||
<span style="padding-left: 10px; color: white;">Terminal</span>
|
||||
<div style="display: flex;">
|
||||
<span class="device-keyboard" tabindex="0" (click)="k.focus()" #k><i class="fa fa-keyboard-o" aria-hidden="true"></i></span>
|
||||
<span class="device-keyboard" tabindex="0" (click)="k.focus()" #k><i class="fa fa-keyboard-o"
|
||||
aria-hidden="true"></i></span>
|
||||
<span style="padding-right: 10px; cursor: pointer; color: white;" (click)="mobileTerminalVisible=false">
|
||||
<i class="fa fa-times"></i></span>
|
||||
<i class="fa fa-times"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
<app-terminal class="term-fullscreen" [socket]="session.socket" [oldText]="session.bufferText" [appId]="session.appId"
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
.search-box{
|
||||
border-bottom: 1px solid green;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.search-text{
|
||||
border: none;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.search-text:focus{
|
||||
outline: none;
|
||||
}
|
|
@ -1,3 +1,97 @@
|
|||
<p>
|
||||
monitoring works!
|
||||
</p>
|
||||
<div
|
||||
style="height: calc(100vh - 55px); width: 100vw; display: flex; flex-direction: column; position: fixed; top: 55px; left: 0px; background: white; z-index: 10;">
|
||||
<div style="display: flex; justify-content: space-around; flex-wrap: wrap; padding: 20px;">
|
||||
<div>
|
||||
<div style="text-align: center; padding: 10px; font-size: 20px;">
|
||||
<span>CPU USAGE {{cpuUsageSet[0][0].toFixed(1)}}%</span>
|
||||
</div>
|
||||
<div class="chart-container" style="height:200px; position: relative; width: 200px;">
|
||||
<canvas baseChart [data]="cpuUsageSet" [labels]="doughnutChartLabels" [chartType]="doughnutChartType" [colors]="colors"
|
||||
[options]="options">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="text-align: center; padding: 10px; font-size: 20px;">
|
||||
<span>MEMORY USAGE {{memoryUsageSet[0][0].toFixed(1)}}%</span>
|
||||
</div>
|
||||
<div class="chart-container" style="height:200px; position: relative; width: 200px;">
|
||||
<canvas baseChart [data]="memoryUsageSet" [labels]="doughnutChartLabels" [chartType]="doughnutChartType" [colors]="colors"
|
||||
[options]="options">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="text-align: center; padding: 10px; font-size: 20px;">
|
||||
<span>DISKSPACE USAGE {{diskUsageSet[0][0].toFixed(1)}}%</span>
|
||||
</div>
|
||||
<div class="chart-container" style="height:200px; position: relative; width: 200px;">
|
||||
<canvas baseChart [data]="diskUsageSet" [labels]="doughnutChartLabels" [chartType]="doughnutChartType" [colors]="colors"
|
||||
[options]="options">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="text-align: center; padding: 10px; font-size: 20px;">
|
||||
<span>SWAP USAGE {{swapUsageSet[0][0].toFixed(1)}}%</span>
|
||||
</div>
|
||||
<div class="chart-container" style="height:200px; position: relative; width: 200px;">
|
||||
<canvas baseChart [data]="swapUsageSet" [labels]="doughnutChartLabels" [chartType]="doughnutChartType" [colors]="colors"
|
||||
[options]="options">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding:20px; padding-bottom: 10px; display: flex; justify-content: space-between;">
|
||||
<div>
|
||||
<span style="line-height: 30px;">Processes</span>
|
||||
</div>
|
||||
<div style="display: flex;">
|
||||
<div style="display: flex; padding-right: 10px;" class="search-box" tabindex="0">
|
||||
<input type="text" class="search-text" placeholder="Search process">
|
||||
<span>
|
||||
<i class="fa fa-search" aria-hidden="true"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary btn-sm mr-2" (click)="killSelectedProcesses()">Kill process</button>
|
||||
<button class="btn btn-primary btn-sm" (click)="getProcStats()">Refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="width: 100%; flex: 1; overflow: auto; border: 1px solid rgb(230,230,230);">
|
||||
<table class="table table-striped table-sm table-borderless" style="position: relative;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">NAME</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">PID</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">PRIORITY</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">CPU USAGE</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">MEMORY</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">VIRTUAL MEMORY</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">USER</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">START TIME</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">STATE</th>
|
||||
<th scope="col" style="position: sticky; top: 0px; background: white;">COMMAND</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let proc of processList">
|
||||
<td><input type="checkbox" [checked]="proc.selected"
|
||||
(change)="proc.selected = !proc.selected" ><span style="padding-left: 10px;">{{proc.name}}</span></td>
|
||||
<td>{{proc.pid}}</td>
|
||||
<td>{{proc.priority}}</td>
|
||||
<td>{{proc.cpuUsage}}</td>
|
||||
<td>{{proc.memoryUsage}}</td>
|
||||
<td>{{proc.vmUsage}}</td>
|
||||
<td>{{proc.user}}</td>
|
||||
<td>{{proc.startTime}}</td>
|
||||
<td>{{proc.state}}</td>
|
||||
<td style="overflow-wrap: break-word; max-width: 200px;">
|
||||
{{proc.command}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
|
@ -1,15 +1,119 @@
|
|||
import { Component, OnInit } from '@angular/core';
|
||||
import { ChartType, ChartOptions } from 'chart.js';
|
||||
import { MultiDataSet, Label, Colors, Color } from 'ng2-charts';
|
||||
import { DataService } from 'src/app/data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-monitoring',
|
||||
templateUrl: './monitoring.component.html',
|
||||
styleUrls: ['./monitoring.component.css']
|
||||
styleUrls: ['./monitoring.component.css'],
|
||||
host: {
|
||||
'(window:resize)': 'onResize($event)'
|
||||
}
|
||||
})
|
||||
export class MonitoringComponent implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
public doughnutChartLabels: Label[] = ['Used', 'Free'];
|
||||
public colors: Color[] = [
|
||||
{
|
||||
backgroundColor: ['Orange', 'SteelBlue'],
|
||||
borderColor: ['Orange', 'SteelBlue']
|
||||
}
|
||||
];
|
||||
public cpuUsageSet: MultiDataSet = [
|
||||
[0, 100]
|
||||
];
|
||||
public memoryUsageSet: MultiDataSet = [
|
||||
[0, 100]
|
||||
];
|
||||
public diskUsageSet: MultiDataSet = [
|
||||
[0, 100]
|
||||
];
|
||||
public swapUsageSet: MultiDataSet = [
|
||||
[0, 100]
|
||||
];
|
||||
public doughnutChartType: ChartType = 'doughnut';
|
||||
public options: ChartOptions = {
|
||||
maintainAspectRatio: false,
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
};
|
||||
|
||||
timer: any;
|
||||
|
||||
processList: any[] = [];
|
||||
|
||||
constructor(private service: DataService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.getStats();
|
||||
this.getProcStats();
|
||||
this.timer = setInterval(() => {
|
||||
this.getStats();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
public getProcStats() {
|
||||
this.service.getProcessList().subscribe((resp: any[]) => {
|
||||
this.processList = resp;
|
||||
for (let proc of this.processList) {
|
||||
proc.selected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public killSelectedProcesses() {
|
||||
let pids: number[] = [];
|
||||
for (let proc of this.processList) {
|
||||
if (proc.selected) {
|
||||
pids.push(proc.pid);
|
||||
}
|
||||
}
|
||||
if (pids.length < 1) {
|
||||
alert("Nothing selected to kill");
|
||||
return;
|
||||
}
|
||||
|
||||
this.service.killProcesses(pids).subscribe((resp: any) => {
|
||||
if (!resp.success) {
|
||||
alert("Failed to kill");
|
||||
} else {
|
||||
this.getProcStats();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
setSelected(item: any, selection: boolean) {
|
||||
item.selected = selection;
|
||||
}
|
||||
|
||||
public getStats() {
|
||||
this.service.getSystemStats().subscribe((resp: any) => {
|
||||
this.cpuUsageSet = [
|
||||
[resp.cpuUsed, resp.cpuFree]
|
||||
];
|
||||
this.memoryUsageSet = [
|
||||
[resp.memoryUsed, resp.memoryFree]
|
||||
];
|
||||
this.diskUsageSet = [
|
||||
[resp.diskUsed, resp.diskFree]
|
||||
];
|
||||
this.swapUsageSet = [
|
||||
[resp.swapUsed, resp.swapFree]
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public chartClicked({ event, active }: { event: MouseEvent, active: {}[] }): void {
|
||||
console.log(event, active);
|
||||
}
|
||||
|
||||
public chartHovered({ event, active }: { event: MouseEvent, active: {}[] }): void {
|
||||
console.log(event, active);
|
||||
}
|
||||
|
||||
onResize(event: any) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue