system monitor added

master
subhra74 2019-07-13 23:42:37 +02:00
parent 371a696fce
commit 33d61b1805
15 changed files with 967 additions and 35 deletions

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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";
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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": {

View File

@ -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]

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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;
}

View File

@ -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>

View File

@ -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) {
}
}