From 33abcaccc5a78b7b12a27f6531b6f1f52d40459e Mon Sep 17 00:00:00 2001 From: Gurkengewuerz Date: Mon, 23 Oct 2017 01:29:40 +0200 Subject: [PATCH] enhancements --- .../java/de/gurkengewuerz/monitoring/GUI.java | 46 ++++++++++++++---- .../monitoring/object/Server.java | 20 ++++++++ .../monitoring/object/ServerStatus.java | 26 +++++++++- src/main/resources/icon.png | Bin 0 -> 18385 bytes 4 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 src/main/resources/icon.png diff --git a/src/main/java/de/gurkengewuerz/monitoring/GUI.java b/src/main/java/de/gurkengewuerz/monitoring/GUI.java index 8689aef..f329b72 100644 --- a/src/main/java/de/gurkengewuerz/monitoring/GUI.java +++ b/src/main/java/de/gurkengewuerz/monitoring/GUI.java @@ -12,18 +12,22 @@ import com.googlecode.lanterna.screen.Screen; import com.googlecode.lanterna.screen.TerminalScreen; import com.googlecode.lanterna.terminal.DefaultTerminalFactory; import com.googlecode.lanterna.terminal.Terminal; +import com.googlecode.lanterna.terminal.swing.SwingTerminalFrame; import de.gurkengewuerz.monitoring.object.MassCommand; import de.gurkengewuerz.monitoring.object.Server; import de.gurkengewuerz.monitoring.object.ServerStatus; import org.apache.commons.validator.routines.InetAddressValidator; import org.pmw.tinylog.Logger; -import org.sqlite.date.DateFormatUtils; +import javax.swing.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -36,13 +40,34 @@ import java.util.List; * Created by gurkengewuerz.de on 21.10.2017. */ public class GUI { + + private HashMap serverlist; + public GUI() { - HashMap serverlist = Server.getServerList(StartUp.getDb()); + serverlist = Server.getServerList(StartUp.getDb()); Poller poller = new Poller(); try { Terminal terminal = new DefaultTerminalFactory() .setTerminalEmulatorTitle("Servermanagment").createTerminal(); - //TODO: Set GUI Icon + if (terminal instanceof SwingTerminalFrame) { + SwingTerminalFrame frame = (SwingTerminalFrame) terminal; + URL iconURL = getClass().getResource("/icon.png"); + frame.setIconImage(new ImageIcon(iconURL).getImage()); + + frame.setSize(930, 500); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + poller.stop(); + StartUp.getDb().closeConnection(); + System.exit(0); + } + }); + + frame.repaint(); + } + terminal.flush(); + Screen screen = new TerminalScreen(terminal); screen.startScreen(); @@ -125,7 +150,7 @@ public class GUI { } }).addTo(rightPanel).takeFocus(); - new Button("Alle auswählen", () -> checkBoxList.getItems().forEach(s -> checkBoxList.setChecked(s, true))).addTo(rightPanel); + new Button("Alle auswählen", () -> checkBoxList.getItems().forEach(s -> checkBoxList.setChecked(s, !checkBoxList.isChecked(s)))).addTo(rightPanel); new Button("Aktion ausführen", () -> { List checkedItems = checkBoxList.getCheckedItems(); @@ -140,7 +165,7 @@ public class GUI { new ActionListDialogBuilder() .setTitle("Aktion ausführen") .setDescription("Was möchtest du unternhmen") - .addAction("Server löschen", () -> { + .addAction("Löschen", () -> { try { PreparedStatement ps = StartUp.getDb().getPreparedStatement("DELETE FROM server WHERE id = ?"); serverarray.forEach(server -> { @@ -158,12 +183,13 @@ public class GUI { } updateList(checkBoxList, serverlist); }) - .addAction("Server Status", () -> { + .addAction("Info", () -> serverarray.forEach(server -> MessageDialog.showMessageDialog(gui, server.getName(), server.toString(), MessageDialogButton.OK))) + .addAction("Status", () -> { BasicWindow windowStatus = getStatusWindow(checkBoxList, serverlist); gui.addWindow(windowStatus); gui.setActiveWindow(windowStatus); }) - .addAction("Server updaten", () -> MassCommand.run("apt-get update && apt-get upgrade -y", gui, serverarray, StartUp.getPrivate_key())) + .addAction("Updaten", () -> MassCommand.run("apt-get update && apt-get upgrade -y", gui, serverarray, StartUp.getPrivate_key())) .addAction("Herunterfahren", () -> MassCommand.run("shutdown -h now", gui, serverarray, StartUp.getPrivate_key())) .addAction("Neustart", () -> MassCommand.run("reboot", gui, serverarray, StartUp.getPrivate_key())) .build() @@ -171,7 +197,7 @@ public class GUI { }).addTo(rightPanel); new Button("Aktualisieren", () -> { - // TODO: serverlist = Server.getServerList(db); + serverlist = Server.getServerList(StartUp.getDb()); updateList(checkBoxList, serverlist); }).addTo(rightPanel); @@ -182,7 +208,7 @@ public class GUI { }).addTo(rightPanel); screen.setCursorPosition(null); - + screen.doResizeIfNecessary(); gui.addWindowAndWait(window); } catch (IOException e) { Logger.error(e); @@ -224,7 +250,7 @@ public class GUI { table.getTableModel().addRow(server.getName(), status.getOS(), status.getCPUcount() + "x " + status.getCPUModel(), - freeMB + "MB/" + totalMB + "MB", DateFormatUtils.format(status.getUptime() * 1000, "HH:mm:ss"), + freeMB + "MB/" + totalMB + "MB", status.getUptimeFormatted(), String.valueOf(status.getLoad()), status.getState().toString()); } // "Name", "OS", "CPU", "RAM", "Uptime", "Last", "Status" diff --git a/src/main/java/de/gurkengewuerz/monitoring/object/Server.java b/src/main/java/de/gurkengewuerz/monitoring/object/Server.java index 5bd472d..fab5373 100644 --- a/src/main/java/de/gurkengewuerz/monitoring/object/Server.java +++ b/src/main/java/de/gurkengewuerz/monitoring/object/Server.java @@ -58,6 +58,26 @@ public class Server { this.status = status; } + public String getAddedFormatted() { + return ServerStatus.getDate(timeAdded, "HH:mm:ss"); + } + + @Override + public String toString() { + return name + " (#" + id + ")" + "\n" + + "------------------" + "\n" + + "Server: " + username + "@" + ip + ":" + port + "\n" + + "Hinzugefügt am: " + getAddedFormatted() + "\n" + + "OS: " + status.getOS() + "\n" + + "CPU Model: " + status.getCPUModel() + "\n" + + "Kerne: " + status.getCPUcount() + "\n" + + "Memory: " + status.getMemFree() + "kb/" + status.getMemTotal() + "kb\n" + + "Last: " + status.getLoad() + "\n" + + "Uptime: " + status.getUptimeFormatted() + "\n" + + "Letzte Aktueallisierung: " + status.getLastPollFormatted() + "\n" + + "Status: " + status.getState().toString(); + } + public SSHManager getSSHSession(String ssh_private) { SSHManager instance = new SSHManager(getUsername(), ssh_private, ip, "", port); String errorMessage = instance.connect(); diff --git a/src/main/java/de/gurkengewuerz/monitoring/object/ServerStatus.java b/src/main/java/de/gurkengewuerz/monitoring/object/ServerStatus.java index e59b4cf..b46bfdd 100644 --- a/src/main/java/de/gurkengewuerz/monitoring/object/ServerStatus.java +++ b/src/main/java/de/gurkengewuerz/monitoring/object/ServerStatus.java @@ -1,5 +1,10 @@ package de.gurkengewuerz.monitoring.object; +import org.sqlite.date.DateFormatUtils; + +import java.text.SimpleDateFormat; +import java.util.Calendar; + /** * Created by gurkengewuerz.de on 21.10.2017. */ @@ -56,7 +61,7 @@ public class ServerStatus { } public State getState() { - return state; + return state == null ? State.OFFLINE : state; } public long getLastpoll() { @@ -98,4 +103,23 @@ public class ServerStatus { public void setLastpoll(long lastpoll) { this.lastpoll = lastpoll; } + + public String getUptimeFormatted() { + return DateFormatUtils.format(uptime * 1000, "HH:mm:ss"); + } + + public String getLastPollFormatted() { + return getDate(lastpoll, "dd.MM.yyyy HH:MM:ss"); + } + + + public static String getDate(long milliSeconds, String dateFormat) { + // Create a DateFormatter object for displaying date in specified format. + SimpleDateFormat formatter = new SimpleDateFormat(dateFormat); + + // Create a calendar object that will convert the date and time value in milliseconds to date. + Calendar calendar = Calendar.getInstance(); + calendar.setTimeInMillis(milliSeconds); + return formatter.format(calendar.getTime()); + } } diff --git a/src/main/resources/icon.png b/src/main/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..13522ec9a04bc69d73ce7de66735dcaaa88322a3 GIT binary patch literal 18385 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 T+u8**kb%L|)z4*}Q$iB}2yt!! literal 0 HcmV?d00001