Python Skripte remote überwachen und steuern [Linux]

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Xbash_Zero
User
Beiträge: 30
Registriert: Montag 19. September 2022, 22:48

Moin,

ich suche eine Software, welche Serverseitig läuft und mit der es ermöglicht wird, Python Scripte/Programme, welche im Hintergrund laufen sollen, zu starten oder stoppen. Auch um zu prüfen, ob das Programm ausgeführt wird oder nicht, quasi remote, weil auf der Konsole wäre das ja mehr oder weniger trivial. Der Hintergrund dazu ist einfach, dass die meisten Programme, die ich schreibe, im Hintergrund laufen und irgendwas machen, ich aber in der Lage sein möchte, die Programme zu stoppen/starten oder zu resetten, falls sich am Code etwas ändert, ohne gleich wieder per SSH auf den Host zu gehen.

Ich bin zwar gerade dabei, mir so etwas in der Art selber zu schreiben, mittels HTML/PHP/JS/Shell, weil ich trotz ausgiebiger Suche nichts Vernünftiges gefunden habe, dennoch möchte ich hier die Frage in den Raum stellen, ob so etwas jemand kennt oder sogar benutzt.

Würde mich über Tipps freuen!

Grüße
Benutzeravatar
grubenfox
User
Beiträge: 432
Registriert: Freitag 2. Dezember 2022, 15:49

Also einen Webserver der die Python-Hintergrundprozesse managt? Was selbst gebautes auf Basis von Django oder Flask (mittels HTML und Python)?

Ich meine ich müsste da noch irgendwelche Namen von fertigen Geschichten wissen, aktuell aber gähnende Leere im Hirn. Eine Suche hat mich dafür eben zu "Cockpit" geführt: https://cockpit-project.org/
Jetzt zum ersten Mal gesehen, kann also nicht sagen ob das was 'vernünftiges' ist.

Eine andere Idee: nichts auf dem Server machen, sondern mittels "Fabric" (https://www.fabfile.org/) vom Client-Rechner auf den Server zugreifen? Gut, das ist im Prinzip nicht anderes als 'per SSH auf den Host zu gehen', aber wenigstens in hübsch und in Python...
Benutzeravatar
sparrow
User
Beiträge: 4195
Registriert: Freitag 17. April 2009, 10:28

@XBashZero: Der Ansatz klingt falsch. Code ändert sich ja nicht magisch von alleine. Das ist der Punkt an dem du ansetzen musst. Stichwort Deployment Prozess.
imonbln
User
Beiträge: 149
Registriert: Freitag 3. Dezember 2021, 17:07

Xbash_Zero hat geschrieben: Donnerstag 22. Juni 2023, 21:16 ich suche eine Software, welche Serverseitig läuft und mit der es ermöglicht wird, Python Scripte/Programme, welche im Hintergrund laufen sollen, zu starten oder stoppen. Auch um zu prüfen, ob das Programm ausgeführt wird oder nicht...
Klingt für mich nach einem systemd Service, ist zwar nicht Python spezifisch, dafür aber gut dokumentiert und millionenfach im Einsatz.
Wahlweise tut es auch jedes andere Init-System, was immer gerade auf deinen Server verfügbar ist.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na er will die ja managen via Webseite oder so. Das bringt systemd nicht mit sich. sparrow hat da schon recht - das ist klassisch deployment (und Monitoring), und zb sowas wie Terraform etc könnte da helfen. Ist aber natürlich auch gleich die ganz dicke Kanone für den Spatz.
imonbln
User
Beiträge: 149
Registriert: Freitag 3. Dezember 2021, 17:07

Erstmal will der das Programme, im Hintergrund laufen und bei Bedarf neu starten, das Pattern heißt, Dämonprozess oder eben (Systemd) Service.

Dass der TO dafür eine Webseite schreibt, hat nichts damit zu tun, dass es eigentlich Prozesse im Hintergrund laufen lassen will. Abgesehen davon wird es wahrscheinlich für den TO immer noch einfacher, wenn er die Prozesse in systemd Unit erzeugt und ggf. eine GUI zusammenbaut, die ein wrapper für systemctl ist.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Es wird nit-picky, aber er redet ja nun davon, dass die schon im Hintergrund laufen. Nicht, dass er dazu einen Weg sucht. Du hast natuerlich trotzdem Recht, dass man dazu systemd benutzen sollte, wenn das die einzige Aufgabe waere. Ausser eben man benutzt stattdessen Docker & Co, weil die einem die remote Orchestrierung ermoeglichen.
Xbash_Zero
User
Beiträge: 30
Registriert: Montag 19. September 2022, 22:48

__deets__ hat geschrieben: Freitag 23. Juni 2023, 11:23 Es wird nit-picky, aber er redet ja nun davon, dass die schon im Hintergrund laufen. Nicht, dass er dazu einen Weg sucht. Du hast natuerlich trotzdem Recht, dass man dazu systemd benutzen sollte, wenn das die einzige Aufgabe waere. Ausser eben man benutzt stattdessen Docker & Co, weil die einem die remote Orchestrierung ermoeglichen.
Hi Deets & Danke!

Über systemd werde ich mal nachdenken ... Docker ist, denke ich für mein Szenario etwas too much ... da müsste ich glaube ich zumindest, für jedes Programm einen eigenen Container bereitstellen? Wie würdest du bei einem Docker Szenario vorgehen?


grubenfox hat geschrieben: Donnerstag 22. Juni 2023, 23:17 Also einen Webserver der die Python-Hintergrundprozesse managt? Was selbst gebautes auf Basis von Django oder Flask (mittels HTML und Python)?

Ich meine ich müsste da noch irgendwelche Namen von fertigen Geschichten wissen, aktuell aber gähnende Leere im Hirn. Eine Suche hat mich dafür eben zu "Cockpit" geführt: https://cockpit-project.org/
Jetzt zum ersten Mal gesehen, kann also nicht sagen ob das was 'vernünftiges' ist.

Eine andere Idee: nichts auf dem Server machen, sondern mittels "Fabric" (https://www.fabfile.org/) vom Client-Rechner auf den Server zugreifen? Gut, das ist im Prinzip nicht anderes als 'per SSH auf den Host zu gehen', aber wenigstens in hübsch und in Python...
Hi grubenfox & Danke!

Das Cockpit schaue ich mal näher an, an so etwas in der Art habe ich gedacht.


Ihr wisst ja, ein Python Script lässt sich auf dem Linux Terminal zB. so starten, damit dieses im Hintergrund läuft und auch die prints & loggings in ein log-file laufen -> "python3 my_script.py &> $LOG_FILE_1 &"

Auf einer zweiten virtuellen Maschine, habe ich ein sh Script, das sieht so aus:

Code: Alles auswählen

#!/bin/bash

# Search for running python processes
ssh user@192.168.1.24 'ps aux | grep python'

(Die SSH Keys wurden vorher schon auf das remote System exportiert)

Bei meiner eigen Entwicklung habe ich mir überlegt, ich schreibe ein frontend mittels hmtl und javascript und ein backend mittels python statt php ... php ist viel zu unflexibel finde ich. Jetzt könnte man via longpoling oder socket bzw. websocket auf das backend zugreifen um dort shell scripte auszuführen oder auszulesen ...



HTML

Hier hänge ich gerne mal an was ich schon hab:

Code: Alles auswählen

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <link href="favicon.png" rel="icon" type="image/png" />
    <title>Server Control</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div class="left-bar"></div>

    <div class="top-bar"></div>

    <div class="tabs-container">
        <div class="green-bar">
            <div class="tabs">
                <button class="tablink" onclick="openTab(event, 'Tab1')">Tab 1</button>
                <button class="tablink" onclick="openTab(event, 'Tab2')">Tab 2</button>
                <button class="tablink" onclick="openTab(event, 'Tab2')">Tab 3</button>
            </div>
        </div>
    </div>

    <div id="Tab1" class="tabcontent">
        <iframe src="tab1_content.html" frameborder="10" scrolling="no" width="600" height="400"></iframe>
    </div>

    <div id="Tab2" class="tabcontent">
        <iframe src="tab2_content.html" frameborder="10" scrolling="no" width="600" height="400"></iframe>
    </div>

    <div id="Tab3" class="tabcontent">
        <iframe src="tab2_content.html" frameborder="10" scrolling="no" width="600" height="400"></iframe>
    </div>




    <div id="output1"></div>
    <div id="output2"></div>






    <script src="worker.js"></script>
    <script src="websocket.js"></script>

</body>
</html>


CSS

Code: Alles auswählen

.left-bar {
    background-color: orange;
    width: 120px;
    position: fixed;
    top: 0;
    left: 0;
    height: 100%;
    z-index: 9999;
    border-top-left-radius: 20px;
    border-bottom-left-radius: 40px; 

}

.top-bar {
    background-color: orange;
    height: 30px;
    position: fixed;
    top: 0;
    left: 120px; 
    width: calc(100% - 120px); 
    z-index: 9999;
    border-top-right-radius: 20px; 

}

.green-bar {
    background-color: green;
    left: 120px;
    height: 50px; 
    position: fixed;
    width: calc(100% - 120px);
    display: flex;
    align-items: center; 
}

/* Tabs */
.tabs-container {
    margin-top: 50px; 
}

.tabs {
    display: flex;
    justify-content: flex-start;
    /* justify-content: center; */
    /* text-align: center; */
    padding: 5px;
    margin-left: 25px; 

}

.tablink {
    background-color: #808080;   /* #4CAF50;   */
    color: white; 
    border: none;
    outline: none;
    cursor: pointer;
    padding: 5px 30px;
    font-size: 15px;
    margin-right: 10px;
    margin-left: 25;

}

.tablink:hover {
    background-color: red;  
}

.tabcontent {
    position: fixed;
    display: none;
    padding: 6px 12px;
    border: 1px solid #ccc;
    margin-top: 100px;
    margin-left: 150px;
    margin-right: 250px;
    width: 900px;
    height: 500px;
}


.tabcontent h3 {
    margin-top: 50;
}

.tabcontent.show {
    display: block;
}



JS - Tabs

Code: Alles auswählen

function openTab(evt, tabName) {
    var i, tabcontent, tablinks;
    tabcontent = document.getElementsByClassName("tabcontent");
    for (i = 0; i < tabcontent.length; i++) {
        tabcontent[i].style.display = "none";
    }
    tablinks = document.getElementsByClassName("tablink");
    for (i = 0; i < tablinks.length; i++) {
        tablinks[i].className = tablinks[i].className.replace(" active", "");
    }
    document.getElementById(tabName).style.display = "block";
    evt.currentTarget.className += " active";
}


JS

Code: Alles auswählen

var socket = new WebSocket("ws://192.168.0.7:8080"); // WebSocket-Verbindung herstellen

socket.onmessage = function(event) {
    var data = JSON.parse(event.data);
    var scriptId = data.scriptId;
    var output = data.output;

    if (scriptId === 1) {
        document.getElementById("output1").innerHTML = output;
    } else if (scriptId === 2) {
        document.getElementById("output2").innerHTML = output;
    }
};

// Funktion zum Senden einer Befehlsanfrage an das Shell-Skript
function sendCommand(scriptId, command) {
    var data = {
        scriptId: scriptId,
        command: command
    };
    socket.send(JSON.stringify(data));
}

Tab1-Content

Code: Alles auswählen

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Tab 1 Inhalt</title>



    <style>


        #text-container {
            position: absolute;
            margin-top: 150px;
            top: 20px;
            font-size: 18px;
            font-weight: bold;
        }




    </style>



</head>

<body>


    <div class="frame-container">
        <form method="post" action="control.php">
            <input type="submit" name="button1" value="Button 1">
            <input type="submit" name="button2" value="Button 2">
            <input type="submit" name="check_script" value="Skript ausführen">
        </form>

        <iframe class="frame-content" src="control.php" frameborder="0"></iframe>
    </div>



    <div id="text-container">
        Python Server Script -_TEXT_-
    </div>


</body>
</html>

__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also was du da mit "im Hintergrund laufen lassen" machst, ist natuerlich so erstmal nicht gut. Denn die sollten dann AfAIK noch nicht mal den logout ueberleben. Dafuer ist in erster Naeherung mindestens mal das von imonbln vorgeschlagenen systemd zu benutzen. Damit koennen die Services sauber gestarted, ueberwacht, und gestoppt werden.

Oder eben tatsaechlich Docker, ja. Wie genau sich das dann aufteilt, haengt von deinen konkreten Programmen ab. Wenn da mehrere sowieso immer zusammen gestartet werden, koennen die auch in einen container. Aber allgemein wird man eher einen Container pro Skript einsetzen.
Xbash_Zero
User
Beiträge: 30
Registriert: Montag 19. September 2022, 22:48

Ok, danke dir!

Ich werde es versuchen und mich wieder melden.
Xbash_Zero
User
Beiträge: 30
Registriert: Montag 19. September 2022, 22:48

Gut, ich habe es zumindest eingerichtet bekommen, allerdings schaffe ich es nicht, dass zB. "~$ systemctl start server.service" nicht ohne Passwort-Abfrage ausgeführt wird. Ok, logisch, man benötigt sudo bzw. root Rechte ... aber für mein Vorhaben ist es notwendig, dass einige meiner Skripte mit meinem normalen User ausgeführt werden können, da müsste es doch evtl. einen Workaround geben?

Hat jemand eine Idee, wie ich das machen kann? Ich werde ebenfalls noch mal eine Recherche starten ...


Edit:

so habe ich es eingerichtet:

Code: Alles auswählen

sudo nano  /etc/systemd/system/

Code: Alles auswählen

[Unit]
Description=Beschreibung des Services
After=network.target

[Service]
User=user
WorkingDirectory=/pfad/zum/programm
ExecStart=/pfad/zu/python /pfad/zum/programm/programm.py
Restart=always

[Install]
WantedBy=multi-user.target

Code: Alles auswählen

sudo systemctl enable programm.service
Benutzeravatar
noisefloor
User
Beiträge: 3857
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

es gibt zwei Wege: zum einem kannst du in einer systemd Service Unit eine Direktive anlegen, unter welchem User diese laufen soll. Default ist ist `root`, `User=otto` würde die Unit als User `otto` starten. Zum anderen gibt es systemd User Service Units. Diese werden (erst) gestartet, wenn der Nutzer, für den die Unit gelegt ist, sich einloggt.

Welcher Weg der besser für dich ist musst du entscheiden.

Gruß, noisefloor
nezzcarth
User
Beiträge: 1636
Registriert: Samstag 16. April 2011, 12:47

Xbash_Zero hat geschrieben: Samstag 24. Juni 2023, 00:09 Gut, ich habe es zumindest eingerichtet bekommen, allerdings schaffe ich es nicht, dass zB. "~$ systemctl start server.service" nicht ohne Passwort-Abfrage ausgeführt wird.
Grundsätzlich gilt unter Linux, dass Prozesse mit dem minimal-notwendigen Set an Berechtigungen laufen sollen. Jeder Daemon – danach klingen deine "Skripte" jedenfalls – läuft zudem unter einem eigenen Systemuser. Dass nur Root und User mit Sudo diese Dienste starten und stoppen können, ist Best Practice; dass du dein Passwort eingeben musst "gehört" so und das sollte man auch nicht umgehen (auch wenn das geht). Für mich klingen deine Beschreibungen jedenfalls teilweise noch etwas unsortiert. Was sind das für Skripte, die da laufen sollen? Welche Rechte brauchen die? Warum soll ein User, der nicht Root ist und dem der Prozess nicht läuft diesen starten/stoppen können? Wäre evtl. ein Userspace Prozessmanager wie supervisord hier eine Lösung für dich?
Xbash_Zero
User
Beiträge: 30
Registriert: Montag 19. September 2022, 22:48

noisefloor hat geschrieben: Samstag 24. Juni 2023, 14:47 Hallo,

es gibt zwei Wege: zum einem kannst du in einer systemd Service Unit eine Direktive anlegen, unter welchem User diese laufen soll. Default ist ist `root`, `User=otto` würde die Unit als User `otto` starten. Zum anderen gibt es systemd User Service Units. Diese werden (erst) gestartet, wenn der Nutzer, für den die Unit gelegt ist, sich einloggt.

Welcher Weg der besser für dich ist musst du entscheiden.

Gruß, noisefloor
Hallo noisefloor & Danke!

Das mit dem User "user" habe ich schon getestet,

Code: Alles auswählen

 ~$ systemctl --user start server.service
leider ebenfalls mit Passworteingabe.

Ich habe es jetzt erstmal so gelöst, dass ich in

Code: Alles auswählen

visudo
einen Eintrag für meinen User gemacht habe

Code: Alles auswählen

user ALL=(ALL) NOPASSWD: /usr/bin/systemctl start server.service, /user/bin/systemctl stop server.service
Ist zwar nicht ganz so elegant aber zumindest können die benötigten Programme mittels sudo usw. als Dienst behandelt werden, ohne eine Password eingeben zu müssen. Wenn ich noch eine bessere Lösung finde oder ich noch Tipps bekomme, die ich auch realisieren kann, probiere ich das gerne aus, und nochmals Danke!


nezzcarth hat geschrieben: Samstag 24. Juni 2023, 16:42
Xbash_Zero hat geschrieben: Samstag 24. Juni 2023, 00:09 Gut, ich habe es zumindest eingerichtet bekommen, allerdings schaffe ich es nicht, dass zB. "~$ systemctl start server.service" nicht ohne Passwort-Abfrage ausgeführt wird.
Grundsätzlich gilt unter Linux, dass Prozesse mit dem minimal-notwendigen Set an Berechtigungen laufen sollen. Jeder Daemon – danach klingen deine "Skripte" jedenfalls – läuft zudem unter einem eigenen Systemuser. Dass nur Root und User mit Sudo diese Dienste starten und stoppen können, ist Best Practice; dass du dein Passwort eingeben musst "gehört" so und das sollte man auch nicht umgehen (auch wenn das geht). Für mich klingen deine Beschreibungen jedenfalls teilweise noch etwas unsortiert. Was sind das für Skripte, die da laufen sollen? Welche Rechte brauchen die? Warum soll ein User, der nicht Root ist und dem der Prozess nicht läuft diesen starten/stoppen können? Wäre evtl. ein Userspace Prozessmanager wie supervisord hier eine Lösung für dich?
Hallo nezzcarth & Danke!

Weiter oben habe ich das doch bereits spezifiziert, ich möchte gerne, dass einige meiner Server-Programme, die in Python geschrieben sind, auf einem Web-Interface angezeigt und gesteuert werden können, ob das Sinn macht oder nicht, spielt erstmal keine Rolle, weil vielleicht noch weitere Features hinzukommen sollen, mal schauen.

Einer der Gründe ist auch, dass manche von den Programmen, von denen die meisten im Entwicklungs- und Teststadium sind, nicht permanent laufen müssen oder wenn ich ein paar Zeilen Code zwischendurch ändere, ich mich nicht für einen Restart auf der Konsole einloggen müsste, usw...

Und was das Thema Sicherheit betrifft, so läuft das Szenario in einem abgesicherten Netzwerk, auf welches kein Zugriff von Außen möglich ist. Auch die internen Sicherheitskonzepte für dieses Netzwerk haben einen hohen Standard. Und solche Spielereien wie ich es hier beschrieben habe und umsetzen möchte, laufen auf keinen Systemkritischen oder Produktiv-System. ;-)

Code: Alles auswählen

Warum soll ein User, der nicht Root ist und dem der Prozess nicht läuft diesen starten/stoppen können?
Auch dazu noch mal die Erklärung zu systemd, es scheint von der Handhabung her besser oder leichter zu sein, wenn ich diese Programme als systemd Prozess handhabe. Ich kann natürlich auch einige meiner Programme mittels

Code: Alles auswählen

python3 programm_server.py &
starten, und das funktioniert auch für manche einfachere Programme, allerdings muss ich die Programme immer wegkillen und das muss dann auch manuell gemacht werden ... Bitte dazu auch mal selber Gedanken machen, Danke! Weil bei einem Py-Script hatte ich unter anderem das Problem, dass dort so um die 10 Child-Processe liefen und ich jeden einzelen und auch die Threads manuell mittels SIGKILL wegkillen musste, aber nur wenn das Programm im Hintergrund lief... das war auf dauer nicht so toll und hatte mich auch gewundert, weil ich eine sigterm exeption implementiert hatte.

Den "Userspace Prozessmanager" schaue ich mir auch noch an, Danke vielmals für den Input!


Und noch eine weitere Frage, welche Python Websocket Bibliothek könnt ihr mir empfehlen? Habe mir websockets angeschaut, ist aber mit asyncio nicht so mein Fall, ich bin mehr so der threading Typ :D
Xbash_Zero
User
Beiträge: 30
Registriert: Montag 19. September 2022, 22:48

Xbash_Zero hat geschrieben: Samstag 24. Juni 2023, 18:29 Und noch eine weitere Frage, welche Python Websocket Bibliothek könnt ihr mir empfehlen? Habe mir websockets angeschaut, ist aber mit asyncio nicht so mein Fall, ich bin mehr so der threading Typ :D
Um mir meine Frage selbst zu beantworten: Seit der Version 10.0 gibt es bei der Python Bibliothek "Websockets", die synchrone Implementierung und dabei können auch asynchrone Techniken wie Threading oder Multiprocessing genutzt werden.


Zur Vollständigkeit, hier meine Implementierung zu diesem Projekt.

Bild

index.html

Code: Alles auswählen

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <link href="favicon.png" rel="icon" type="image/png" />
    <title>Server Control</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div class="left-bar"></div>

    <div class="top-bar"></div>


    <div class="tabs-container">
        <div class="green-bar">
            <div class="tabs">
                <button class="tablink" onclick="openTab(event, 'Tab1')">Tab 1</button>
                <button class="tablink" onclick="openTab(event, 'Tab2')">Tab 2</button>
                <button class="tablink" onclick="openTab(event, 'Tab2')">Tab 3</button>
            </div>
        </div>
    </div>



    <div id="Tab1" class="tabcontent">

        <div id="service-status">

            <div id="service-status-text">Service Status keep_alive</div>


            <div class="image-wrapper">
              <img id="myImage1" src="off.png" alt="state_led">
            </div>

        </div>
        <div id="service-control">
            <button class="toggel-button" onclick="start_keep_alive()">start</button>
            <button class="toggel-button" onclick="stop_keep_alive()">stop</button>
            <button class="toggel-button" onclick="restart_keep_alive()">restart</button>
        </div>


        <div id="service-status">

            <div id="service-status-text">Service Status Websocket</div>


            <div class="image-wrapper">
              <img id="myImage2" src="off.png" alt="state_led">
            </div>

        </div>
        <div id="service-control">
            <button class="toggel-button" onclick="start_websocket()">start</button>
            <button class="toggel-button" onclick="stop_websocket()">stop</button>
            <button class="toggel-button" onclick="restart_websocket()">restart</button>
        </div>


        <div id="service-status">

            <div id="service-status-text">Service Status ClickBot</div>

            <div class="image-wrapper">
              <img id="myImage3" src="off.png" alt="state_led">
            </div>

        </div>
        <div id="service-control">
            <button class="toggel-button" onclick="start_clickbot()">start</button>
            <button class="toggel-button" onclick="stop_clickbot()">stop</button>
            <button class="toggel-button" onclick="restart_clickbot()">restart</button>
        </div>


    </div>




    <div id="Tab2" class="tabcontent">

        <iframe src="tab2_content.html" frameborder="10" scrolling="no" width="600" height="400"></iframe>

    </div>



    <div id="Tab3" class="tabcontent">

        <iframe src="tab2_content.html" frameborder="10" scrolling="no" width="600" height="400"></iframe>

    </div>





    <script src="worker.js"></script>
    <script src="websocket.js"></script>

</body>
</html>



tab2_content.html (Optional bei verschiedenen Inhalten)

Code: Alles auswählen

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Tab 2 Inhalt</title>

</head>

<body>
    <div class="frame-container">
        <form method="post" action="control.php">
            <input type="submit" name="button1" value="Button 1">
            <input type="submit" name="button2" value="Button 2">
            <input type="submit" name="check_script" value="Skript ausführen">
        </form>

        <iframe class="frame-content" src="control.php" frameborder="0" scrolling="no"></iframe>
    </div>
</body>
</html>



style.css

Code: Alles auswählen

/* CSS für die Leisten */
.left-bar {
    background-color: orange;
    width: 120px;
    position: fixed;
    top: 0;
    left: 0;
    height: 100%;
    z-index: 9999;
    border-top-left-radius: 20px; 
    border-bottom-left-radius: 40px; 

}

.top-bar {
    background-color: orange;
    height: 30px;
    position: fixed;
    top: 0;
    left: 120px; 
    width: calc(100% - 120px); 
    z-index: 9999;
    border-top-right-radius: 20px; 

}

.green-bar {
    background-color: green;
    left: 120px;
    height: 50px; 
    position: fixed;
    width: calc(100% - 120px);
    display: flex;
    align-items: center; 
}

/* Tabs */
.tabs-container {
    margin-top: 50px; 
}

.tabs {
    display: flex;
    justify-content: flex-start;
    /* justify-content: center; */
    /* text-align: center; */
    padding: 5px;
    margin-left: 25px; 

}

.tablink {
    background-color: #808080;   /* #4CAF50;   */
    color: white; 
    border: none;
    outline: none;
    cursor: pointer;
    padding: 5px 30px; 
    font-size: 15px;
    margin-right: 10px;
    margin-left: 25;

}

.tablink:hover {
    background-color: red;  /* #45a049; */
}

.tabcontent {
    position: fixed;
    display: none;
    padding: 6px 12px;
    border: 2px solid #ccc;
    margin-top: 100px;
    margin-left: 150px;
    margin-right: 250px;
    width: 900px;
    height: 700px;
}


.tabcontent h3 {
    margin-top: 50;
}

.tabcontent.show {
    display: block;
}



#output1 {
    position: absolute;
    top: 250px;
    left: 200px;
    width: 300px;
    height: 400px;
    margin-left: 150px;
}

#output2 {
    position: absolute;
    top: 250px;
    left: 600px;
    width: 300px;
    height: 400px;
    margin-left: 150px;
}

.output-container {
    width: 600px;
    height: 400px;
    overflow: auto; 
    border: 1px solid #000; 
    white-space: pre-wrap;
}



#service-status {
    margin-top: 50px;
    text-align: center;
}

#service-status img {
    width: 30px;
    height: 30px;
    /* box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); */
    border-radius: 25px;
}

#service-status-text {
    font-size: 18px;
    color: #555;
    margin-top: 10px;
}

#service-control {
    margin-top: 50px;
    text-align: center;
}

#service-control button {
    padding: 10px 20px;
    margin: 5px;
    border: none;
    background-color: #808080;
    color: white;
    font-size: 15px;
    cursor: pointer;
    border-radius: 5px;
    transition: background-color 0.3s ease;
}

#service-control button:hover {
    background-color: #555;
}






worker.js

Code: Alles auswählen

function openTab(evt, tabName) {
    var i, tabcontent, tablinks;
    tabcontent = document.getElementsByClassName("tabcontent");
    for (i = 0; i < tabcontent.length; i++) {
        tabcontent[i].style.display = "none";
    }
    tablinks = document.getElementsByClassName("tablink");
    for (i = 0; i < tablinks.length; i++) {
        tablinks[i].className = tablinks[i].className.replace(" active", "");
    }
    document.getElementById(tabName).style.display = "block";
    evt.currentTarget.className += " active";
}


websocket.js

Code: Alles auswählen

var socket = new WebSocket("ws://192.168.0.7:8080"); // WebSocket-Verbindung herstellen

var outputText1 = "";
var outputText2 = "";



    socket.onerror = function(event) {
        console.error("WebSocket error observed:", event);
    };


    // Nach dem Öffnen der WebSocket-Verbindung
    socket.onopen = function(event) {
        console.log("Connected to server");
        sendCommand(0, 'get');  

        setInterval(function() {
            sendCommand(0, 'get'); 
        }, 15000);
    };


    // Funktion zum Senden einer Befehlsanfrage an das Shell-Skript
    function sendCommand(scriptId, command) {
        var data = {
            scriptId: scriptId,
            command: command
        };
        socket.send(JSON.stringify(data));
    }


    socket.onmessage = function(event) {
        console.log("Received message:", event.data);
        try {
            var data = JSON.parse(event.data);
            if (data.scriptId === 0 && data.output) {
                var service = data.output.service;
                var state = data.output.state;
                if (service === "keep_alive_server.service") {
                    updateState("myImage1", state);
                } else if (service === "websocket_server.service") {
                    updateState("myImage2", state);
                } else if (service === "clickbot_server.service") {
                    updateState("myImage3", state);
                }
            }
        } catch (e) {
            console.error("Error handling message:", e);
        }
    };



    async function start_keep_alive() {
        await socket.send(JSON.stringify({"service": "keep_alive", "command": "start"}));
    }

    async function stop_keep_alive() {
        await socket.send(JSON.stringify({"service": "keep_alive", "command": "stop"}));
    }

    async function restart_keep_alive() {
        await socket.send(JSON.stringify({"service": "keep_alive", "command": "restart"}));
    }

    async function start_websocket() {
        await socket.send(JSON.stringify({"service": "websocket", "command": "start"}));
    }

    async function stop_websocket() {
        await socket.send(JSON.stringify({"service": "websocket", "command": "stop"}));
    }

    async function restart_websocket() {
        await socket.send(JSON.stringify({"service": "websocket", "command": "restart"}));
    }

    async function start_clickbot() {
        await socket.send(JSON.stringify({"service": "clickbot", "command": "start"}));
    }

    async function stop_clickbot() {
        await socket.send(JSON.stringify({"service": "clickbot", "command": "stop"}));
    }

    async function restart_clickbot() {
        await socket.send(JSON.stringify({"service": "clickbot", "command": "restart"}));
    }




    function updateState(imageId, state) {
        var image = document.getElementById(imageId);
        if (state === "active") {
            image.src = "on.png";
        } else {
            image.src = "off.png";
        }
    }



websockets_server.py (Synchrone Version)

Code: Alles auswählen

import websockets.sync.server
import subprocess
import json
import os
import signal

import multiprocessing
import threading

import time
import logging

logging.basicConfig(level=logging.INFO)


queue_to_process = multiprocessing.Queue()
queue_from_process = multiprocessing.Queue()

# Mapping from script-IDs to processes
processes = {}

def send_output(websocket, process, script_id):
    while True:
        logging.info(f"send_output called for script {script_id}")

        output = process.stdout.readline().decode('utf-8').strip()
        if not output:
            break
        try:
            output_json = json.loads(output)
            logging.info(f"Script {script_id} output: {output_json}")
            message = {'scriptId': script_id, 'output': output_json}
            logging.info("websocket.send")

            logging.info(f"Sending message: {message}")

            websocket.send(json.dumps(message))

            logging.info(f"Message sent successfully")

            time.sleep(0.1)

        except json.JSONDecodeError:
            logging.warning(f"Ignoring invalid JSON output: {output}")
        except Exception as e:
            logging.error(f"Error sending message: {e}", exc_info=True)
            pass



def handle_message(websocket):
    logging.info("Client connected")  # Log when a client connects

    for message in websocket:
        data = json.loads(message)
        script_id = data.get('scriptId')
        command = data.get('command')

        if command == 'get':
            logging.info(f"Received get command for script {script_id}")

            # Start the script
            process = subprocess.Popen(['python3', 'execute_script5.py'],
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT)
            processes[script_id] = process

            # Create a background task to send the output
            threading.Thread(target=send_output, args=(websocket, process, script_id)).start()


        elif command == 'start':
            logging.info(f"Received start command for service {script_id}")
            service = data.get('service')
            if service == 'keep_alive':
                os.system(f"ssh user@192.168.0.24 sudo systemctl start keep_alive_server.service")
            elif service == 'websocket':
                os.system(f"ssh user@192.168.0.24 sudo systemctl start websocket_server.service")
            elif service == 'clickbot':
                os.system(f"ssh user@192.168.0.24 sudo systemctl start clickbot.service")
            else:
                logging.warning(f"Invalid service: {service}")


        elif command == 'stop':
            logging.info(f"Received stop command for service {script_id}")
            service = data.get('service')
            if service == 'keep_alive':
                os.system(f"ssh user@192.168.0.24 sudo systemctl stop keep_alive_server.service")
            elif service == 'websocket':
                os.system(f"ssh user@192.168.0.24 sudo systemctl stop websocket_server.service")
            elif service == 'clickbot':
                os.system(f"ssh user@192.168.0.24 sudo systemctl stop clickbot.service")
            else:
                logging.warning(f"Invalid service: {service}")


        elif command == 'restart':
            logging.info(f"Received restart command for service {script_id}")
            service = data.get('service')
            if service == 'keep_alive':
                os.system(f"ssh user@192.168.0.24 sudo systemctl restart keep_alive_server.service")
            elif service == 'websocket':
                os.system(f"ssh user@192.168.0.24 sudo systemctl restart websocket_server.service")
            elif service == 'clickbot':
                os.system(f"ssh user@192.168.0.24 sudo systemctl restart clickbot.service")
            else:
                logging.warning(f"Invalid service: {service}")




#def process_interface(queue_in, queue_out):
#    while True:
#        
	  # do not block 
#        if not queue_in.empty():
#            message = queue_in.get()
#            print(f"Process received message: {message}")

#            queue_out.put("Response from process")
#            time.sleep(10)


with websockets.sync.server.serve(handle_message, '192.168.0.7', 8080) as server:
    server.serve_forever()





execute_script5.py

Code: Alles auswählen

import subprocess
import re
import json
import time

def execute_shell_script():
    process = subprocess.Popen(['/bin/sh', '/var/www/html/control/script3.sh'], 
                               stdout=subprocess.PIPE, 
                               stderr=subprocess.PIPE)  # Capture stderr separately

    stdout, stderr = process.communicate()  # Get stdout and stderr
    if process.returncode != 0:
        print(f"Script execution failed with error: {stderr.decode('utf-8')}")
        return process.returncode

    return stdout.decode('utf-8')

def parse_service_status(output):
    service_sections = re.split(r'● |○ ', output)

    for section in service_sections[1:]:  
        match = re.search(r'(.*?\.service)', section)
        if match:
            name = match.group(1)
            status = "active" if "Active: active" in section else "inactive"
            print(json.dumps({"service": name, "state": status}))

def print_service_status(service_status):
    for service in service_status:
        print(json.dumps(service))

def execute_and_parse():
    output = execute_shell_script()

    time.sleep(1)

    if isinstance(output, str):

        parse_service_status(output)
    else:
        print("No valid output to parse")

if __name__ == "__main__":
    execute_and_parse()

script3.sh

Code: Alles auswählen

#!/bin/bash
# Script for remote access

#output=$(ssh user@192.168.0.24 'ps aux | grep python')
#echo "$output"

output=$(ssh user@192.168.0.24 sudo systemctl status keepalive_server.service)
echo "$output"

output=$(ssh user@192.168.0.24 systemctl status websocket_server.service)
echo "$output"


keepalive_status=$(ssh user@192.168.0.24 sudo systemctl is-active keepalive_server.service)
if [ "$keepalive_status" = "active" ]; then
    echo "keepalive_server.service is active"
else
    echo "keepalive_server.service is inactive"
fi

websocket_status=$(ssh user@192.168.0.24 sudo systemctl is-active websocket_server.service)
if [ "$websocket_status" = "active" ]; then
    echo "websocket_server.service is active"
else
    echo "websocket_server.service is inactive"
fi


Antworten