EventGhost/EventGhost

View on GitHub
plugins/Ping/__init__.py

Summary

Maintainability
D
2 days
Test Coverage
# -*- coding: utf-8 -*-
#
# plugins/Ping/__init__.py
#
# This file is a plugin for EventGhost.
# Copyright © 2005-2020 EventGhost Project <http://www.eventghost.net/>
#
# EventGhost is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 2 of the License, or (at your option)
# any later version.
#
# EventGhost is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with EventGhost. If not, see <http://www.gnu.org/licenses/>.

import eg

eg.RegisterPlugin(
  name = "Ping",
  author = (
    "miljbee",
    "Sem;colon",
  ),
  version = "0.0.3",
  guid = "{59C111B9-EF9E-48DA-A57D-6231BC9AB2FD}",
  kind = "other",
  description = "This plugin generates events when an host become available or unavailable on your LAN. It uses the ping commands of windows. Please, have a look at the readme file !"
)

import os
import subprocess
from threading import Event, Thread, Lock
prefix="Ping"

class PingPlugin(eg.PluginBase):
    def __init__(self):
        self.hosts = {}
        self.AddAction(OnePing)
        self.AddAction(AddHost)
        self.AddAction(RemoveHost)
        self.AddAction(GetHostsStatus)

    def __start__(self, pingString):
        self.pingString=str(pingString)
        print "Ping is started with parameter: " + pingString
        for host in self.hosts:
            print "Starting thread " + self.hosts[host].name
            self.hosts[host].StartThread()


    def __stop__(self):
        print "Ping is stopped."
        for host in self.hosts:
            print "Stopping thread " + self.hosts[host].name
            self.hosts[host].StopThread()
        for host in self.hosts:
            print "Joining thread " + self.hosts[host].name
            self.hosts[host].pingThread.join()


    def __close__(self):
        print "Ping is closed."
        while len(self.hosts)>0:
            for host in self.hosts:
                print "Removing " + self.hosts[host].name
                self.RemoveHost(host)
                break


    def Configure(self, pingString="=32"):
        helpString = "This plugin launch ping commands to know if hosts are alive or dead.\n"
        helpString = helpString + "Ping commands are the same you would use in the command line interpreter. ping 192.168.0.1 -w 500 as an exemple.\n"
        helpString = helpString + "Unfortunately, the output of the ping command is not the same depending on the version of windows and its localization.\n"
        helpString = helpString + "Thus, you should provide here a string, that may be found in the output of the ping command when a host respond to the request.\n"
        helpString = helpString + "This string will be compared against the output of the ping command. If it's found, the host is considered to be alive.\n"
        helpString = helpString + "If it's not found, then the host is considered to be dead.\n"
        helpString = helpString + "You should test some ping commands in your command prompt to determine which string best fits with your os !\n"

        panel = eg.ConfigPanel(self)
        helpLabel=panel.StaticText(helpString)
        pingStringEdit=panel.TextCtrl(pingString)
        panel.AddLine(helpLabel)
        panel.AddLine("String to match : ",pingStringEdit)

        while panel.Affirmed():
            panel.SetResult(pingStringEdit.GetValue())

    def RemoveHost(self, host=""):
        hostToDel=self.hosts.get(host,None)
        if hostToDel:
            #print "--- " + hostToDel.GetThreadState()
            if hostToDel.GetThreadState()=="Thread is running":
                hostToDel.StopThread()
                hostToDel.pingThread.join()
            elif hostToDel.GetThreadState()=="Thread is finishing his job":
                hostToDel.pingThread.join()
            elif hostToDel.GetThreadState()=="Problem !":
                print "Oups !"
            else:
                print "--- " + hostToDel.GetThreadState()
            del self.hosts[host]
            del hostToDel
        else:
            print "Ping Plugin : /!\\ " + host + " isn't in my list !"
        if len(self.hosts)==0:
            print "Ping Plugin : the list of watched hosts is now empty !"

    def AddHost(self, host):
        print "Ping Plugin : Adding host " + host.name + " to the list of managed hosts."
        hostToDel=self.hosts.get(host.name,None)
        if hostToDel:
            print "Ping Plugin : /!\\ Host already exists, it will be replaced"
            self.RemoveHost(host.name)
        self.hosts[host.name]=host
        self.hosts[host.name].StartThread()

class OnePing(eg.ActionBase):
    name = "One Ping Now"
    description = "Send one only ping command to the specified host. The state of the hosts is returned in eg.result. Optionnaly, you can generate an event."

    def __call__(self, hostName,hostFriendlyName,pingDelay,sendEvent,eventAlive,eventDead):
        global prefix
        pingCmd = os.popen("ping " + hostName + " -w " + str(pingDelay) + " -n 1","r")
        if pingCmd.read().find(self.plugin.pingString)>-1:
            hostStatus="alive"
        else:
            hostStatus="dead"
        if eventAlive=="":
            if hostFriendlyName=="":
                eventAlive=hostName + "_IS_ALIVE"
            else:
                eventAlive=hostFriendlyName + "_IS_ALIVE"
        if eventDead=="":
            if hostFriendlyName=="":
                eventDead=hostName + "_IS_DEAD"
            else:
                eventDead=hostFriendlyName + "_IS_DEAD"
        if sendEvent:
            if hostStatus=="alive":
                eg.TriggerEvent(prefix=prefix,suffix=eventAlive)
            else:
                eg.TriggerEvent(prefix=prefix,suffix=eventDead)
        return (hostStatus=="alive")

    def Configure(self,hostName="",hostFriendlyName="",pingDelay=200,sendEvent=False,eventAlive="",eventDead=""):
        panel = eg.ConfigPanel()
        hostNameEdit=panel.TextCtrl(hostName)
        hostFriendlyNameEdit=panel.TextCtrl(hostFriendlyName)
        pingDelayEdit=panel.SpinIntCtrl(pingDelay, max=5000)
        sendEventEdit=panel.CheckBox(sendEvent)
        eventAliveEdit=panel.TextCtrl(eventAlive)
        eventDeadEdit=panel.TextCtrl(eventDead)
        panel.AddLine("Host name: ",hostNameEdit)
        panel.AddLine("Host friendly name: ",hostFriendlyNameEdit)
        panel.AddLine("Time to wait for the host response to the ping (milliseconds): ",pingDelayEdit)
        panel.AddLine()
        panel.AddLine("Generate an event ",sendEventEdit)
        panel.AddLine("if checked will generate an event corresponding to the host response, otherwise, eg.result will be set to true/false")
        panel.AddLine()
        panel.AddLine("Event string to fire if the host responds: ",eventAliveEdit)
        panel.AddLine("Event string to fire if the host doesn't responds:",eventDeadEdit)
        while panel.Affirmed():
            panel.SetResult(
                hostNameEdit.GetValue(),
                hostFriendlyNameEdit.GetValue(),
                pingDelayEdit.GetValue(),
                sendEventEdit.GetValue(),
                eventAliveEdit.GetValue(),
                eventDeadEdit.GetValue()
                )


class AddHost(eg.ActionBase):
    name = "Add Host"
    description = "Adds a host to the list of watched host. Once done, you'll get an event when the state of the host changes."

    def __call__(self,hostName,hostFriendlyName,pingDelay,eventAlive,eventDead,delayEventAlive,delayEventDead):
        host = Host(hostName,hostFriendlyName,pingDelay,eventAlive,eventDead,delayEventAlive,delayEventDead,self.plugin.pingString)
        self.plugin.AddHost(host)

    def Configure(self,hostName="",hostFriendlyName="",pingDelay=200,eventAlive="",eventDead="",delayEventAlive=1,delayEventDead=1):
        panel = eg.ConfigPanel()
        hostNameEdit=panel.TextCtrl(hostName)
        hostFriendlyNameEdit=panel.TextCtrl(hostFriendlyName)
        pingDelayEdit=panel.SpinIntCtrl(pingDelay, max=5000)
        eventAliveEdit=panel.TextCtrl(eventAlive)
        eventDeadEdit=panel.TextCtrl(eventDead)
        delayEventAliveEdit=panel.SpinIntCtrl(delayEventAlive, min=1)
        delayEventDeadEdit=panel.SpinIntCtrl(delayEventDead, min=1)


        panel.AddLine("Host Name: ",hostNameEdit)
        panel.AddLine("Host Friendly Name: ",hostFriendlyNameEdit)
        panel.AddLine("Ping delay (ms): ",pingDelayEdit)
        panel.AddLine("Name of the event to fire when host become alive: ",eventAliveEdit)
        panel.AddLine("Name of the event to fire when host become dead: ",eventDeadEdit)
        panel.AddLine("The next two settings will delay the events. As an exemple, if you set the first one to 5,\nyou will get the alive event only when five successive ping commands will be successfull")
        panel.AddLine("Number of successive successfull ping to fire the alive event: ",delayEventAliveEdit)
        panel.AddLine("Number of successive unsuccessfull ping to fire the dead event: ",delayEventDeadEdit)
        while panel.Affirmed():
            panel.SetResult(
                hostNameEdit.GetValue(),
                hostFriendlyNameEdit.GetValue(),
                pingDelayEdit.GetValue(),
                eventAliveEdit.GetValue(),
                eventDeadEdit.GetValue(),
                delayEventAliveEdit.GetValue(),
                delayEventDeadEdit.GetValue(),
            )

class RemoveHost(eg.ActionBase):
    name="Remove Host"
    description="Removes a host from the list of watched hosts. Once done, you won't receive any more event from this host."
    def __call__(self,hostName):
        self.plugin.RemoveHost(hostName)

    def Configure(self,hostName=""):
        panel = eg.ConfigPanel()
        hostNameEdit=panel.TextCtrl(hostName)
        panel.AddLine("Enter the name of the host you wish to remove, not the friendly name !")
        panel.AddLine("Host name: ",hostNameEdit)
        while panel.Affirmed():
            panel.SetResult(
                hostNameEdit.GetValue())

class GetHostsStatus(eg.ActionBase):
    name="Get Hosts Status"
    description="Set eg.result with a python dict containing the config and status of all watched hosts. The key of the dict is the host name.\n"
    description+="Each dict entry is a string where values are separated by a coma.\n the values are :\n"
    description+="hostName,hostFriendlyName,pingDelay,eventAlive,eventDead,delayEventAlive,delayEventDead, status,lastPingResult.\n"
    description+="Here is an exemple of a python script you could run jus after this action :\n"
    description+="hosts=eg.result\n"
    description+="for host in hosts:\n"
    description+="    print host + \" : \" + hosts[host]\n"
    description+="    hostData=hosts[host].split(\",\")\n"
    description+="    for idx in range(0,len(hostData)):\n"
    description+="        print hostData[idx]\n"
    def __call__(self):
        res={}
        for hostName in self.plugin.hosts:
            host=self.plugin.hosts[hostName]
            res[hostName]=""
            res[hostName]+=host.name + ","
            res[hostName]+=host.friendlyName + ","
            res[hostName]+=str(host.pingDelay) + ","
            res[hostName]+=host.eventAlive + ","
            res[hostName]+=host.eventDead + ","
            res[hostName]+=str(host.delayEventAlive) + ","
            res[hostName]+=str(host.delayEventDead) + ","
            res[hostName]+=host.GetStatus() + ","
            res[hostName]+=host.GetLastPingResult()
        return res


class Host:
    def __init__(self,name,friendlyName,pingDelay,eventAlive,eventDead,delayEventAlive,delayEventDead,pingString):
        self.pingDelay = pingDelay
        self.name = name
        self.friendlyName = friendlyName
        self.status = "unknown"
        self.lastPingResult = "unknown"
        global prefix

        if eventAlive=="":
            if friendlyName=="":
                self.eventAlive=name+"_IS_ALIVE"
            else:
                self.eventAlive=friendlyName+"_IS_ALIVE"
        else:
            self.eventAlive = eventAlive

        if eventDead=="":
            if friendlyName=="":
                self.eventDead=name+"_IS_DEAD"
            else:
                self.eventDead=friendlyName+"_IS_DEAD"
        else:
            self.eventDead = eventDead

        self.delayEventAlive=delayEventAlive
        self.delayEventDead=delayEventDead
        self.pingString = str(pingString)

        self.pingThread = None
        self.stopPingThreadEvent = Event()
        self.lock = Lock()

    def PingThread(self,stopPingThreadEvent):
        print "Ping Plugin : Thread " + self.name + " is starting ! "
        eventsAlive=0
        eventsDead=0
        startupinfo = subprocess.STARTUPINFO()
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
        pingCmd = subprocess.Popen(["ping",self.name,"-4","-t","-w",str(self.pingDelay)], stdout=subprocess.PIPE, stderr=None,startupinfo=startupinfo)
        pingCmd.wShowWindow = 0
        while not stopPingThreadEvent.isSet():
            output = pingCmd.stdout.readline()
            #print output
            if output.find(self.pingString)>-1:
            #print "    Thread " + self.name + " is pinging !"
                eventsAlive+=1
                eventsDead=0
                self.lock.acquire()
                try:
                    self.lastPingResult="alive"
                    if eventsAlive>=self.delayEventAlive:
                        if self.status!="alive":
                            self.status="alive"
                            eg.TriggerEvent(prefix=prefix,suffix=self.eventAlive)
                finally:
                    self.lock.release()
            else:
                eventsAlive=0
                eventsDead+=1
                self.lock.acquire()
                try:
                    self.lastPingResult="dead"
                    if eventsDead>=self.delayEventDead:
                        if self.status!="dead":
                            self.status="dead"
                            eg.TriggerEvent(prefix=prefix,suffix=self.eventDead)
                finally:
                    self.lock.release()
        print "Ping Plugin : Thread " + self.name + " is ending ! "
        pingCmd.terminate()
        self.stopPingThreadEvent.clear()
        #print "Thread " + self.name + " has finished his job !"

    def GetStatus(self):
        self.lock.acquire()
        try:
            status =self.status
        finally:
            self.lock.release()
        return status

    def GetLastPingResult(self):
        self.lock.acquire()
        try:
            res=self.lastPingResult
        finally:
            self.lock.release()
        return res


    def GetThreadState(self):
        if self.pingThread.isAlive() and not self.stopPingThreadEvent.isSet():
            return "Thread is running"
        elif self.pingThread.isAlive() and self.stopPingThreadEvent.isSet():
            return "Thread is finishing his job"
        elif (not self.pingThread.isAlive()) and (not self.stopPingThreadEvent.isSet()):
            return "Thread is ready to be started"
        else:
            return "Problem !"

    def StopThread(self):
        self.stopPingThreadEvent.set()

    def StartThread(self):
        if self.stopPingThreadEvent.isSet():
            #print "/!\\ Problem : stopPingThreadEvent Is Set ..."
            self.stopPingThreadEvent.clear()
        self.pingThread = Thread(target=self.PingThread,args=(self.stopPingThreadEvent,))
        self.pingThread.start()

    def Print(self):
        print self.name
        print self.friendlyName
        print self.status
        print self.eventAlive
        print self.eventDead