|
|
import subprocess, json, psutil, time, os, win32security, sys #Get input argument
|
|
|
from . import Server
|
|
|
from . import Timer
|
|
|
from . import Processor
|
|
|
from . import BackwardCompatibility # Backward compatibility from v1.1.13
|
|
|
from . import Core
|
|
|
|
|
|
# ATTENTION! HERE IS NO Relative import because it will be imported dynamically
|
|
|
# All function check the flag SessionIsWindowResponsibleBool == True else no cammand is processed
|
|
|
# All functions can return None, Bool or Dict { "IsSuccessful": True }
|
|
|
from .RobotRDPActive import CMDStr # Create CMD Strings
|
|
|
from .RobotRDPActive import Connector # RDP API
|
|
|
from .RobotRDPActive import ConnectorExceptions # Exceptions
|
|
|
|
|
|
#from .Settings import Settings
|
|
|
import importlib
|
|
|
from importlib import util
|
|
|
import threading # Multi-threading for RobotRDPActive
|
|
|
from .RobotRDPActive import RobotRDPActive #Start robot rdp active
|
|
|
from .RobotScreenActive import Monitor #Start robot screen active
|
|
|
import uuid # Generate uuid
|
|
|
import datetime # datetime
|
|
|
|
|
|
#Единый глобальный словарь (За основу взять из Settings.py)
|
|
|
global gSettingsDict
|
|
|
|
|
|
# Defs to use in orchestrator
|
|
|
def OSCredentialsVerify(inUserStr, inPasswordStr, inDomainStr=""): ## Verify credentials in windows
|
|
|
try:
|
|
|
hUser = win32security.LogonUser(
|
|
|
inUserStr,inDomainStr, inPasswordStr,
|
|
|
win32security.LOGON32_LOGON_NETWORK, win32security.LOGON32_PROVIDER_DEFAULT
|
|
|
)
|
|
|
except win32security.error:
|
|
|
return False
|
|
|
else:
|
|
|
return True
|
|
|
|
|
|
def OSCMD(inCMDStr): ## OS send command in shell locally
|
|
|
lCMDCode = "cmd /c " + inCMDStr
|
|
|
subprocess.Popen(lCMDCode)
|
|
|
lResultCMDRun = 1 # os.system(lCMDCode)
|
|
|
return lResultCMDRun
|
|
|
|
|
|
def OrchestratorRestart(inGSettings=None): ## Orchestrator restart
|
|
|
OrchestratorSessionSave(inGSettings=inGSettings) # Dump RDP List in file json
|
|
|
if inGSettings is not None:
|
|
|
lL = inGSettings["Logger"]
|
|
|
if lL: lL.info(f"Do restart")
|
|
|
# Restart session
|
|
|
os.execl(sys.executable, os.path.abspath(__file__), *sys.argv)
|
|
|
sys.exit(0)
|
|
|
|
|
|
def OrchestratorSessionSave(inGSettings=None): ## Orchestrator session save
|
|
|
# Dump RDP List in file json
|
|
|
lFile = open("_SessionLast_RDPList.json", "w", encoding="utf-8")
|
|
|
lFile.write(json.dumps(inGSettings["RobotRDPActive"]["RDPList"])) # dump json to file
|
|
|
lFile.close() # Close the file
|
|
|
if inGSettings is not None:
|
|
|
lL = inGSettings["Logger"]
|
|
|
if lL: lL.info(
|
|
|
f"Orchestrator has dump the RDP list before the restart. The RDP List is {inGSettings['RobotRDPActive']['RDPList']}")
|
|
|
return True
|
|
|
|
|
|
## GSettings defs
|
|
|
def GSettingsKeyListValueSet(inGSettings, inValue, inKeyList=[]): # Set value in GSettings by the key list
|
|
|
lDict = inGSettings
|
|
|
for lItem2 in inKeyList[:-1]:
|
|
|
#Check if key - value exists
|
|
|
if lItem2 in lDict:
|
|
|
pass
|
|
|
else:
|
|
|
lDict[lItem2]={}
|
|
|
lDict=lDict[lItem2]
|
|
|
lDict[inKeyList[-1]] = inValue #Set value
|
|
|
return True
|
|
|
|
|
|
def GSettingsKeyListValueGet(inGSettings, inKeyList=[]): # Get the value from the GSettings by the key list
|
|
|
lDict = inGSettings
|
|
|
for lItem2 in inKeyList[:-1]:
|
|
|
#Check if key - value exists
|
|
|
if lItem2 in lDict:
|
|
|
pass
|
|
|
else:
|
|
|
lDict[lItem2]={}
|
|
|
lDict=lDict[lItem2]
|
|
|
return lDict.get(inKeyList[-1],None)
|
|
|
|
|
|
def GSettingsKeyListValueAppend(inGSettings, inValue, inKeyList=[]): # Append value in GSettings by the key list
|
|
|
lDict = inGSettings
|
|
|
for lItem2 in inKeyList[:-1]:
|
|
|
#Check if key - value exists
|
|
|
if lItem2 in lDict:
|
|
|
pass
|
|
|
else:
|
|
|
lDict[lItem2]={}
|
|
|
lDict=lDict[lItem2]
|
|
|
lDict[inKeyList[-1]].append(inValue) #Set value
|
|
|
return True
|
|
|
|
|
|
def GSettingsKeyListValueOperatorPlus(inGSettings, inValue, inKeyList=[]): # Operator plus value in GSettings by the key list
|
|
|
lDict = inGSettings
|
|
|
for lItem2 in inKeyList[:-1]:
|
|
|
#Check if key - value exists
|
|
|
if lItem2 in lDict:
|
|
|
pass
|
|
|
else:
|
|
|
lDict[lItem2]={}
|
|
|
lDict=lDict[lItem2]
|
|
|
lDict[inKeyList[-1]] += inValue #Set value
|
|
|
return True
|
|
|
|
|
|
## Process defs
|
|
|
def ProcessIsStarted(inProcessNameWOExeStr): # Check if process is started
|
|
|
'''
|
|
|
Check if there is any running process that contains the given name processName.
|
|
|
'''
|
|
|
#Iterate over the all the running process
|
|
|
for proc in psutil.process_iter():
|
|
|
try:
|
|
|
# Check if process name contains the given name string.
|
|
|
if inProcessNameWOExeStr.lower() in proc.name().lower():
|
|
|
return True
|
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
|
pass
|
|
|
return False;
|
|
|
|
|
|
def ProcessStart(inPathStr, inArgList, inStopProcessNameWOExeStr=None): # Start process locally [optional: if task name is not started]
|
|
|
lStartProcessBool = True
|
|
|
if inStopProcessNameWOExeStr is not None: #Check if process running
|
|
|
lCheckTaskName = inStopProcessNameWOExeStr
|
|
|
if len(lCheckTaskName)>4:
|
|
|
if lCheckTaskName[-4:].upper() != ".EXE":
|
|
|
lCheckTaskName = lCheckTaskName+".exe"
|
|
|
else:
|
|
|
lCheckTaskName = lCheckTaskName+".exe"
|
|
|
#Check if process exist
|
|
|
if not ProcessIsStarted(inProcessNameWOExeStr=lCheckTaskName): lStartProcessBool=True
|
|
|
|
|
|
if lStartProcessBool == True: # Start if flag is true
|
|
|
lItemArgs=[inPathStr]
|
|
|
lItemArgs.extend(inArgList)
|
|
|
subprocess.Popen(lItemArgs,shell=True)
|
|
|
|
|
|
def ProcessStop(inProcessNameWOExeStr, inCloseForceBool, inUserNameStr = "%username%"): # Stop process
|
|
|
# Support input arg if with .exe
|
|
|
lProcessNameWExeStr = inProcessNameWOExeStr
|
|
|
if len(lProcessNameWExeStr) > 4:
|
|
|
if lProcessNameWExeStr[-4:].upper() != ".EXE":
|
|
|
lProcessNameWExeStr = lProcessNameWExeStr + ".exe"
|
|
|
else:
|
|
|
lProcessNameWExeStr = lProcessNameWExeStr + ".exe"
|
|
|
# Flag Force
|
|
|
lActivityCloseCommand = 'taskkill /im ' + lProcessNameWExeStr
|
|
|
if inCloseForceBool == True:
|
|
|
lActivityCloseCommand += " /F"
|
|
|
# None - all users, %username% - current user, another str - another user
|
|
|
if inUserNameStr is not None:
|
|
|
lActivityCloseCommand += f' /fi "username eq {inUserNameStr}"'
|
|
|
# Kill process
|
|
|
os.system(lActivityCloseCommand)
|
|
|
|
|
|
#Check activity of the list of processes
|
|
|
def ProcessListGet(inProcessNameWOExeList=[]):
|
|
|
'''Get list of running process sorted by Memory Usage and filtered by inProcessNameWOExeList'''
|
|
|
lMapUPPERInput = {} # Mapping for processes WO exe
|
|
|
lResult = {"ProcessWOExeList":[],"ProcessDetailList":[]}
|
|
|
# Create updated list for quick check
|
|
|
lProcessNameWOExeList = []
|
|
|
for lItem in inProcessNameWOExeList:
|
|
|
if lItem is not None:
|
|
|
lProcessNameWOExeList.append(f"{lItem.upper()}.EXE")
|
|
|
lMapUPPERInput[f"{lItem.upper()}.EXE"]= lItem
|
|
|
# #
|
|
|
# Iterate over the list
|
|
|
for proc in psutil.process_iter():
|
|
|
try:
|
|
|
# Fetch process details as dict
|
|
|
pinfo = proc.as_dict(attrs=['pid', 'name', 'username'])
|
|
|
pinfo['vms'] = proc.memory_info().vms / (1024 * 1024)
|
|
|
pinfo['NameWOExeUpperStr'] = pinfo['name'][:-4].upper()
|
|
|
# Add if empty inProcessNameWOExeList or if process in inProcessNameWOExeList
|
|
|
if len(lProcessNameWOExeList)==0 or pinfo['name'].upper() in lProcessNameWOExeList:
|
|
|
pinfo['NameWOExeStr'] = lMapUPPERInput[pinfo['name'].upper()]
|
|
|
lResult["ProcessDetailList"].append(pinfo) # Append dict to list
|
|
|
lResult["ProcessWOExeList"].append(pinfo['NameWOExeStr'])
|
|
|
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
|
|
|
pass
|
|
|
return lResult
|
|
|
|
|
|
# Python def - start module function
|
|
|
def PythonStart(inModulePathStr, inDefNameStr, inArgList=[], inArgDict={}, inLogger = None): # Python import module and start def
|
|
|
try:
|
|
|
lModule=importlib.import_module(inModulePathStr) #Подключить модуль для вызова
|
|
|
lFunction=getattr(lModule,inDefNameStr) #Найти функцию
|
|
|
return lFunction(*inArgList,**inArgDict)
|
|
|
except Exception as e:
|
|
|
if inLogger: inLogger.exception("Loop activity error: module/function not founded")
|
|
|
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
# RDPSession
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
# Create new RDPSession in RobotRDPActive
|
|
|
def RDPSessionConnect(inGSettings, inRDPSessionKeyStr, inHostStr, inPortStr, inLoginStr, inPasswordStr):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionConnect, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inHostStr": inHostStr, "inPortStr": inPortStr,
|
|
|
"inLoginStr": inLoginStr, "inPasswordStr": inPasswordStr, }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else: # In processor - do execution
|
|
|
# ATTENTION - dont connect if RDP session is exist
|
|
|
if inRDPSessionKeyStr not in inGSettings["RobotRDPActive"]["RDPList"]:
|
|
|
lRDPConfigurationItem = { # Init the configuration item
|
|
|
"Host": inHostStr, # Host address, example "77.77.22.22"
|
|
|
"Port": inPortStr, # RDP Port, example "3389"
|
|
|
"Login": inLoginStr, # Login, example "test"
|
|
|
"Password": inPasswordStr, # Password, example "test"
|
|
|
"Screen": {
|
|
|
"Width": 1680, # Width of the remote desktop in pixels, example 1680
|
|
|
"Height": 1050, # Height of the remote desktop in pixels, example 1050
|
|
|
# "640x480" or "1680x1050" or "FullScreen". If Resolution not exists set full screen, example
|
|
|
"FlagUseAllMonitors": False, # True or False, example False
|
|
|
"DepthBit": "32" # "32" or "24" or "16" or "15", example "32"
|
|
|
},
|
|
|
"SharedDriveList": ["c"], # List of the Root sesion hard drives, example ["c"]
|
|
|
###### Will updated in program ############
|
|
|
"SessionHex": "77777sdfsdf77777dsfdfsf77777777", # Hex is created when robot runs, example ""
|
|
|
"SessionIsWindowExistBool": False, # Flag if the RDP window is exist, old name "FlagSessionIsActive". Check every n seconds , example False
|
|
|
"SessionIsWindowResponsibleBool": False, # Flag if RDP window is responsible (recieve commands). Check every nn seconds. If window is Responsible - window is Exist too , example False
|
|
|
"SessionIsIgnoredBool": False # Flag to ignore RDP window False - dont ignore, True - ignore, example False
|
|
|
}
|
|
|
inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr] = lRDPConfigurationItem # Add item in RDPList
|
|
|
Connector.Session(lRDPConfigurationItem) # Create the RDP session
|
|
|
Connector.SystemRDPWarningClickOk() # Click all warning messages
|
|
|
return True
|
|
|
|
|
|
# Disconnect the RDP session
|
|
|
def RDPSessionDisconnect(inGSettings, inRDPSessionKeyStr, inBreakTriggerProcessWOExeList = []):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionDisconnect, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inBreakTriggerProcessWOExeList": inBreakTriggerProcessWOExeList }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else: # In processor - do execution
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
if lSessionHex:
|
|
|
lProcessListResult = {"ProcessWOExeList":[],"ProcessDetailList":[]}
|
|
|
if len(inBreakTriggerProcessWOExeList) > 0:
|
|
|
lProcessListResult = ProcessListGet(inProcessNameWOExeList=inBreakTriggerProcessWOExeList) # Run the task manager monitor
|
|
|
if len(lProcessListResult["ProcessWOExeList"]) == 0: # Start disconnect if no process exist
|
|
|
inGSettings["RobotRDPActive"]["RDPList"].pop(inRDPSessionKeyStr,None)
|
|
|
Connector.SessionClose(inSessionHexStr=lSessionHex)
|
|
|
Connector.SystemRDPWarningClickOk() # Click all warning messages
|
|
|
return True
|
|
|
|
|
|
# RDP Session reconnect
|
|
|
def RDPSessionReconnect(inGSettings, inRDPSessionKeyStr):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionReconnect, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lRDPConfigurationItem = inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr]
|
|
|
RDPSessionDisconnect(inRDPSessionKeyStr=inRDPSessionKeyStr) # Disconnect the RDP
|
|
|
# Add item in RDPList
|
|
|
inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr] = lRDPConfigurationItem
|
|
|
# Create the RDP session
|
|
|
Connector.Session(lRDPConfigurationItem)
|
|
|
return True
|
|
|
|
|
|
# Stop track the RDP session. Current def dont kill RDP session - only stop to track it (it can give )
|
|
|
def RDPSessionMonitorStop(inGSettings, inRDPSessionKeyStr):
|
|
|
lResult = True
|
|
|
inGSettings["RobotRDPActive"]["RDPList"].pop(inRDPSessionKeyStr,None) # Remove item from RDPList
|
|
|
return lResult
|
|
|
|
|
|
# Logoff the RDP session
|
|
|
def RDPSessionLogoff(inGSettings, inRDPSessionKeyStr, inBreakTriggerProcessWOExeList = []):
|
|
|
lResult = True
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionLogoff, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inBreakTriggerProcessWOExeList": inBreakTriggerProcessWOExeList }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lCMDStr = "shutdown -L" # CMD logoff command
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
if lSessionHex:
|
|
|
lProcessListResult = {"ProcessWOExeList":[],"ProcessDetailList":[]}
|
|
|
if len(inBreakTriggerProcessWOExeList) > 0:
|
|
|
lProcessListResult = ProcessListGet(inProcessNameWOExeList=inBreakTriggerProcessWOExeList) # Run the task manager monitor
|
|
|
if len(lProcessListResult["ProcessWOExeList"]) == 0: # Start logoff if no process exist
|
|
|
# Run CMD - dont crosscheck because CMD dont return value to the clipboard when logoff
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="RUN", inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
inGSettings["RobotRDPActive"]["RDPList"].pop(inRDPSessionKeyStr,None) # Remove item from RDPList
|
|
|
return lResult
|
|
|
|
|
|
# Check RDP Session responsibility TODO NEED DEV + TEST
|
|
|
def RDPSessionResponsibilityCheck(inGSettings, inRDPSessionKeyStr):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionResponsibilityCheck, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lRDPConfigurationItem = inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr] # Get the alias
|
|
|
# set the fullscreen
|
|
|
# ATTENTION!!! Session hex can be updated!!!
|
|
|
Connector.SessionScreenFull(inSessionHex=lRDPConfigurationItem["SessionHex"], inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
time.sleep(1)
|
|
|
# Check RDP responsibility
|
|
|
lDoCheckResponsibilityBool = True
|
|
|
lDoCheckResponsibilityCountMax = 20
|
|
|
lDoCheckResponsibilityCountCurrent = 0
|
|
|
while lDoCheckResponsibilityBool:
|
|
|
# Check if counter is exceed - raise exception
|
|
|
if lDoCheckResponsibilityCountCurrent >= lDoCheckResponsibilityCountMax:
|
|
|
pass
|
|
|
#raise ConnectorExceptions.SessionWindowNotResponsibleError("Error when initialize the RDP session - RDP window is not responding!")
|
|
|
# Check responding
|
|
|
lDoCheckResponsibilityBool = not Connector.SystemRDPIsResponsible(inSessionHexStr = lRDPConfigurationItem["SessionHex"])
|
|
|
# Wait if is not responding
|
|
|
if lDoCheckResponsibilityBool:
|
|
|
time.sleep(3)
|
|
|
# increase the couter
|
|
|
lDoCheckResponsibilityCountCurrent+=1
|
|
|
return True
|
|
|
|
|
|
# Start process if it is not running
|
|
|
def RDPSessionProcessStartIfNotRunning(inGSettings, inRDPSessionKeyStr, inProcessNameWEXEStr, inFilePathStr, inFlagGetAbsPathBool=True):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionProcessStartIfNotRunning, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inProcessNameWEXEStr": inProcessNameWEXEStr, "inFilePathStr": inFilePathStr, "inFlagGetAbsPathBool": inFlagGetAbsPathBool }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lResult = True
|
|
|
lCMDStr = CMDStr.ProcessStartIfNotRunning(inProcessNameWEXEStr, inFilePathStr, inFlagGetAbsPath= inFlagGetAbsPathBool)
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="CROSSCHECK", inLogger=inGSettings["Logger"],
|
|
|
inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
|
|
|
|
|
|
def RDPSessionCMDRun(inGSettings, inRDPSessionKeyStr, inCMDStr, inModeStr="CROSSCHECK"):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionCMDRun, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inCMDStr": inCMDStr, "inModeStr": inModeStr }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lResult = True
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=inCMDStr, inModeStr=inModeStr, inLogger=inGSettings["Logger"],
|
|
|
inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
# Create CMD str to stop process
|
|
|
def RDPSessionProcessStop(inGSettings, inRDPSessionKeyStr, inProcessNameWEXEStr, inFlagForceCloseBool):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionProcessStop, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inProcessNameWEXEStr": inProcessNameWEXEStr, "inFlagForceCloseBool": inFlagForceCloseBool }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lResult = True
|
|
|
lCMDStr = f'taskkill /im "{inProcessNameWEXEStr}" /fi "username eq %USERNAME%"'
|
|
|
if inFlagForceCloseBool:
|
|
|
lCMDStr+= " /F"
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="CROSSCHECK", inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
# Send file from Host to Session RDP using shared drive in RDP
|
|
|
def RDPSessionFileStoredSend(inGSettings, inRDPSessionKeyStr, inHostFilePathStr, inRDPFilePathStr):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionFileStoredSend, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inHostFilePathStr": inHostFilePathStr, "inRDPFilePathStr": inRDPFilePathStr }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lResult = True
|
|
|
lCMDStr = CMDStr.FileStoredSend(inHostFilePath = inHostFilePathStr, inRDPFilePath = inRDPFilePathStr)
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr, {}).get("SessionHex", None)
|
|
|
#lSessionHex = inGlobalDict["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr]["SessionHex"]
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="LISTEN", inClipboardTimeoutSec = 120, inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
# Recieve file from Session RDP to Host using shared drive in RDP
|
|
|
def RDPSessionFileStoredRecieve(inGSettings, inRDPSessionKeyStr, inRDPFilePathStr, inHostFilePathStr):
|
|
|
# Check thread
|
|
|
if not Core.IsProcessorThread(inGSettings=inGSettings):
|
|
|
if inGSettings["Logger"]: inGSettings["Logger"].warning(f"RDP def was called not from processor queue - activity will be append in the processor queue.")
|
|
|
lResult = {
|
|
|
"Def": RDPSessionFileStoredRecieve, # def link or def alias (look gSettings["Processor"]["AliasDefDict"])
|
|
|
"ArgList": [], # Args list
|
|
|
"ArgDict": {"inRDPSessionKeyStr": inRDPSessionKeyStr, "inRDPFilePathStr": inRDPFilePathStr, "inHostFilePathStr": inHostFilePathStr }, # Args dictionary
|
|
|
"ArgGSettings": "inGSettings", # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
"ArgLogger": None # Name of GSettings attribute: str (ArgDict) or index (for ArgList)
|
|
|
}
|
|
|
inGSettings["ProcessorDict"]["ActivityList"].append(lResult)
|
|
|
else:
|
|
|
lResult = True
|
|
|
lCMDStr = CMDStr.FileStoredRecieve(inRDPFilePath = inRDPFilePathStr, inHostFilePath = inHostFilePathStr)
|
|
|
# Calculate the session Hex
|
|
|
lSessionHex = inGSettings["RobotRDPActive"]["RDPList"].get(inRDPSessionKeyStr,{}).get("SessionHex", None)
|
|
|
# Run CMD
|
|
|
if lSessionHex:
|
|
|
Connector.SessionCMDRun(inSessionHex=lSessionHex, inCMDCommandStr=lCMDStr, inModeStr="LISTEN", inClipboardTimeoutSec = 120, inLogger=inGSettings["Logger"], inRDPConfigurationItem=inGSettings["RobotRDPActive"]["RDPList"][inRDPSessionKeyStr])
|
|
|
return lResult
|
|
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
# # # # # Start orchestrator
|
|
|
# # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
# Interval gsettings auto cleaner
|
|
|
def GSettingsAutocleaner(inGSettings):
|
|
|
while True:
|
|
|
time.sleep(inGSettings["Autocleaner"]["IntervalSecFloat"]) # Wait for the next iteration
|
|
|
lL = inGSettings["Logger"]
|
|
|
if lL: lL.info(f"Autocleaner is running") # Info
|
|
|
lNowDatetime = datetime.datetime.now() # Get now time
|
|
|
# Clean old items in Client > Session > TechnicalSessionGUIDCache
|
|
|
lTechnicalSessionGUIDCacheNew = {}
|
|
|
for lItemKeyStr in inGSettings["Client"]["Session"]["TechnicalSessionGUIDCache"]:
|
|
|
lItemValue = inGSettings["Client"]["Session"]["TechnicalSessionGUIDCache"][lItemKeyStr]
|
|
|
if (lNowDatetime - lItemValue["InitDatetime"]).total_seconds() < inGSettings["Client"]["Session"]["LifetimeSecFloat"]: # Add if lifetime is ok
|
|
|
lTechnicalSessionGUIDCacheNew[lItemKeyStr]=lItemValue # Lifetime is ok - set
|
|
|
else:
|
|
|
if lL: lL.debug(f"Client > Session > TechnicalSessionGUIDCache > lItemKeyStr: Lifetime is expired. Remove from gSettings") # Info
|
|
|
inGSettings["Client"]["Session"]["TechnicalSessionGUIDCache"] = lTechnicalSessionGUIDCacheNew # Set updated Cache
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
|
|
from .. import __version__ # Get version from the package
|
|
|
# Main def for orchestrator
|
|
|
def Orchestrator(inGSettings):
|
|
|
#mGlobalDict = Settings.Settings(sys.argv[1])
|
|
|
gSettingsDict = inGSettings # Alias for old name in alg
|
|
|
inGSettings["VersionStr"] = __version__
|
|
|
#Logger alias
|
|
|
lL = gSettingsDict["Logger"]
|
|
|
if lL: lL.info("Link the gSettings in submodules") #Logging
|
|
|
Processor.gSettingsDict = gSettingsDict
|
|
|
Timer.gSettingsDict = gSettingsDict
|
|
|
Timer.Processor.gSettingsDict = gSettingsDict
|
|
|
Server.gSettingsDict = gSettingsDict
|
|
|
Server.ProcessorOld.gSettingsDict = gSettingsDict # Backward compatibility
|
|
|
|
|
|
# Check _SessionLast_RDPList.json in working directory. if exist - load into gsettings
|
|
|
# GSettings
|
|
|
#"RobotRDPActive": {
|
|
|
# "RDPList": {
|
|
|
if os.path.exists("_SessionLast_RDPList.json"):
|
|
|
lFile = open("_SessionLast_RDPList.json", "r", encoding="utf-8")
|
|
|
lSessionLastRDPList = json.loads(lFile.read())
|
|
|
lFile.close() # Close the file
|
|
|
os.remove("_SessionLast_RDPList.json") # remove the temp file
|
|
|
gSettingsDict["RobotRDPActive"]["RDPList"]=lSessionLastRDPList # Set the last session dict
|
|
|
if lL: lL.warning(f"RDP Session List was restored from previous Orchestrator session")
|
|
|
|
|
|
#Инициализация настроечных параметров
|
|
|
lDaemonLoopSeconds=gSettingsDict["Scheduler"]["ActivityTimeCheckLoopSeconds"]
|
|
|
lDaemonActivityLogDict={} #Словарь отработанных активностей, ключ - кортеж (<activityType>, <datetime>, <processPath || processName>, <processArgs>)
|
|
|
lDaemonLastDateTime=datetime.datetime.now()
|
|
|
gSettingsDict["Server"]["WorkingDirectoryPathStr"] = os.getcwd() # Set working directory in g settings
|
|
|
|
|
|
# Init SettingsUpdate defs from file list (after RDP restore)
|
|
|
lSettingsUpdateFilePathList = gSettingsDict.get("OrchestratorStart", {}).get("DefSettingsUpdatePathList",[])
|
|
|
lSubmoduleFunctionName = "SettingsUpdate"
|
|
|
lSettingsPath = "\\".join(os.path.join(os.getcwd(), __file__).split("\\")[:-1])
|
|
|
for lModuleFilePathItem in lSettingsUpdateFilePathList: # Import defs with try catch
|
|
|
try: # Try to init - go next if error and log in logger
|
|
|
lModuleName = lModuleFilePathItem[0:-3]
|
|
|
lFileFullPath = os.path.join(lSettingsPath, lModuleFilePathItem)
|
|
|
lTechSpecification = importlib.util.spec_from_file_location(lModuleName, lFileFullPath)
|
|
|
lTechModuleFromSpec = importlib.util.module_from_spec(lTechSpecification)
|
|
|
lTechSpecificationModuleLoader = lTechSpecification.loader.exec_module(lTechModuleFromSpec)
|
|
|
if lSubmoduleFunctionName in dir(lTechModuleFromSpec):
|
|
|
# Run SettingUpdate function in submodule
|
|
|
getattr(lTechModuleFromSpec, lSubmoduleFunctionName)(gSettingsDict)
|
|
|
except Exception as e:
|
|
|
if lL: lL.exception(f"Error when init .py file in orchestrator '{lModuleFilePathItem}'. Exception is below:")
|
|
|
|
|
|
# Turn on backward compatibility
|
|
|
BackwardCompatibility.Update(inGSettings= gSettingsDict)
|
|
|
|
|
|
#Инициализация сервера
|
|
|
lThreadServer = Server.RobotDaemonServer("ServerThread", gSettingsDict)
|
|
|
lThreadServer.start()
|
|
|
if lL: lL.info("Web server has been started") #Logging
|
|
|
|
|
|
# Init the RobotScreenActive in another thread
|
|
|
lRobotScreenActiveThread = threading.Thread(target= Monitor.CheckScreen)
|
|
|
lRobotScreenActiveThread.daemon = True # Run the thread in daemon mode.
|
|
|
lRobotScreenActiveThread.start() # Start the thread execution.
|
|
|
if lL: lL.info("Robot Screen active has been started") #Logging
|
|
|
|
|
|
# Init the RobotRDPActive in another thread
|
|
|
lRobotRDPActiveThread = threading.Thread(target= RobotRDPActive.RobotRDPActive, kwargs={"inGSettings":gSettingsDict})
|
|
|
lRobotRDPActiveThread.daemon = True # Run the thread in daemon mode.
|
|
|
lRobotRDPActiveThread.start() # Start the thread execution.
|
|
|
if lL: lL.info("Robot RDP active has been started") #Logging
|
|
|
|
|
|
# Init autocleaner in another thread
|
|
|
lAutocleanerThread = threading.Thread(target= GSettingsAutocleaner, kwargs={"inGSettings":gSettingsDict})
|
|
|
lAutocleanerThread.daemon = True # Run the thread in daemon mode.
|
|
|
lAutocleanerThread.start() # Start the thread execution.
|
|
|
if lL: lL.info("Autocleaner thread has been started") #Logging
|
|
|
|
|
|
# Orchestrator start activity
|
|
|
if lL: lL.info("Orchestrator start activity run") #Logging
|
|
|
for lActivityItem in gSettingsDict["OrchestratorStart"]["ActivityList"]:
|
|
|
Processor.ActivityListOrDict(lActivityItem)
|
|
|
|
|
|
if lL: lL.info("Scheduler loop start") #Logging
|
|
|
gDaemonActivityLogDictRefreshSecInt = 10 # The second period for clear lDaemonActivityLogDict from old items
|
|
|
gDaemonActivityLogDictLastTime = time.time() # The second perioad for clean lDaemonActivityLogDict from old items
|
|
|
while True:
|
|
|
lCurrentDateTime = datetime.datetime.now()
|
|
|
#Циклический обход правил
|
|
|
lFlagSearchActivityType=True
|
|
|
# Periodically clear the lDaemonActivityLogDict
|
|
|
if time.time()-gDaemonActivityLogDictLastTime>=gDaemonActivityLogDictRefreshSecInt:
|
|
|
gDaemonActivityLogDictLastTime = time.time() # Update the time
|
|
|
for lIndex, lItem in enumerate(lDaemonActivityLogDict):
|
|
|
if lItem["ActivityEndDateTime"] and lCurrentDateTime<=lItem["ActivityEndDateTime"]:
|
|
|
pass
|
|
|
# Activity is actual - do not delete now
|
|
|
else:
|
|
|
# remove the activity - not actual
|
|
|
lDaemonActivityLogDict.pop(lIndex,None)
|
|
|
lIterationLastDateTime = lDaemonLastDateTime # Get current datetime before iterator (need for iterate all activities in loop)
|
|
|
# Iterate throught the activity list
|
|
|
for lIndex, lItem in enumerate(gSettingsDict["Scheduler"]["ActivityTimeList"]):
|
|
|
# Prepare GUID of the activity
|
|
|
lGUID = None
|
|
|
if "GUID" in lItem and lItem["GUID"]:
|
|
|
lGUID = lItem["GUID"]
|
|
|
else:
|
|
|
lGUID = str(uuid.uuid4())
|
|
|
lItem["GUID"]=lGUID
|
|
|
|
|
|
#Проверка дней недели, в рамках которых можно запускать активность
|
|
|
lItemWeekdayList=lItem.get("WeekdayList", [0, 1, 2, 3, 4, 5, 6])
|
|
|
if lCurrentDateTime.weekday() in lItemWeekdayList:
|
|
|
if lFlagSearchActivityType:
|
|
|
#######################################################################
|
|
|
#Branch 1 - if has TimeHH:MM
|
|
|
#######################################################################
|
|
|
if "TimeHH:MM" in lItem:
|
|
|
#Вид активности - запуск процесса
|
|
|
#Сформировать временной штамп, относительно которого надо будет проверять время
|
|
|
#часовой пояс пока не учитываем
|
|
|
lActivityDateTime=datetime.datetime.strptime(lItem["TimeHH:MM"],"%H:%M")
|
|
|
lActivityDateTime=lActivityDateTime.replace(year=lCurrentDateTime.year,month=lCurrentDateTime.month,day=lCurrentDateTime.day)
|
|
|
#Убедиться в том, что время наступило
|
|
|
if (
|
|
|
lActivityDateTime>=lDaemonLastDateTime and
|
|
|
lCurrentDateTime>=lActivityDateTime):
|
|
|
# Log info about activity
|
|
|
if lL: lL.info(f"Scheduler:: Activity is started. Scheduler item: {lItem}") #Logging
|
|
|
# Do the activity
|
|
|
Processor.ActivityListOrDict(lItem["Activity"])
|
|
|
lIterationLastDateTime = datetime.datetime.now() # Set the new datetime for the new processor activity
|
|
|
#######################################################################
|
|
|
#Branch 2 - if TimeHH:MMStart, TimeHH:MMStop, ActivityIntervalSeconds
|
|
|
#######################################################################
|
|
|
if "TimeHH:MMStart" in lItem and "TimeHH:MMStop" in lItem and "ActivityIntervalSeconds" in lItem:
|
|
|
#Сформировать временной штамп, относительно которого надо будет проверять время
|
|
|
#часовой пояс пока не учитываем
|
|
|
lActivityDateTime=datetime.datetime.strptime(lItem["TimeHH:MMStart"],"%H:%M")
|
|
|
lActivityDateTime=lActivityDateTime.replace(year=lCurrentDateTime.year,month=lCurrentDateTime.month,day=lCurrentDateTime.day)
|
|
|
lActivityTimeEndDateTime=datetime.datetime.strptime(lItem["TimeHH:MMStop"],"%H:%M")
|
|
|
lActivityTimeEndDateTime=lActivityTimeEndDateTime.replace(year=lCurrentDateTime.year,month=lCurrentDateTime.month,day=lCurrentDateTime.day)
|
|
|
#Убедиться в том, что время наступило
|
|
|
if (
|
|
|
lCurrentDateTime<lActivityTimeEndDateTime and
|
|
|
lCurrentDateTime>=lActivityDateTime and
|
|
|
(lGUID,lActivityDateTime) not in lDaemonActivityLogDict):
|
|
|
#Запись в массив отработанных активностей
|
|
|
lDaemonActivityLogDict[(lGUID,lActivityDateTime)]={"ActivityStartDateTime":lCurrentDateTime, "ActivityEndDateTime":lActivityTimeEndDateTime}
|
|
|
#Запуск циклической процедуры
|
|
|
Timer.activityLoopStart(lItem["ActivityIntervalSeconds"], lActivityTimeEndDateTime, lItem["Activity"])
|
|
|
lDaemonLastDateTime = lIterationLastDateTime # Set the new datetime for the new processor activity
|
|
|
#Уснуть до следующего прогона
|
|
|
time.sleep(lDaemonLoopSeconds)
|
|
|
|
|
|
# Backward compatibility below to 1.2.0
|
|
|
def __deprecated_orchestrator_start__():
|
|
|
#Call Settings function from argv[1] file
|
|
|
################################################
|
|
|
lSubmoduleFunctionName = "Settings"
|
|
|
lFileFullPath = sys.argv[1]
|
|
|
lModuleName = (lFileFullPath.split("\\")[-1])[0:-3]
|
|
|
lTechSpecification = importlib.util.spec_from_file_location(lModuleName, lFileFullPath)
|
|
|
lTechModuleFromSpec = importlib.util.module_from_spec(lTechSpecification)
|
|
|
lTechSpecificationModuleLoader = lTechSpecification.loader.exec_module(lTechModuleFromSpec)
|
|
|
gSettingsDict = None
|
|
|
if lSubmoduleFunctionName in dir(lTechModuleFromSpec):
|
|
|
# Run SettingUpdate function in submodule
|
|
|
gSettingsDict = getattr(lTechModuleFromSpec, lSubmoduleFunctionName)()
|
|
|
#################################################
|
|
|
Orchestrator(inGSettings=gSettingsDict) # Call the orchestrator
|