def spawnImmediateTask(self, jobid, taskid_complex=None, params=None): ''' Run task immediately using basic configuration of job @param jobid:int @param taskid_complex: string @param params: string ''' job = Job.getByJobID(jobid) if not job: raise ValueError("Job(id:%d) is not existed or not enabled." % (jobid)) if params : job.set_command(job._substituteReservedWord(params)) if taskid_complex: task = Task(job.get_jobid(), uuid.uuid1().hex, datetime.now(), job.getCommandToExcute(), job.get_retry(), job, job.get_depend(), taskid_complex) else: task = Task(job.get_jobid(), uuid.uuid1().hex, datetime.now(), job.getCommandToExcute(), job.get_retry(), job, job.get_depend()) t = TaskRunner(0, task, self._status, self, params) t.daemon = True task.save() self._status.add_waiting_task(task, t) t.start() return task
def spawnTasks(self, start_time, end_time): ''' Spawn thread based on Job's timer. According to start_time and end_time,Job satisfied the job will be wrapped as Task which launched by Timer thread.For Timer accept delay as its first parameter,we convert job's timer to delay seconds here. These Timer tasks will be started in main thread. ''' tasks = [] jobs = [] self._lock.acquire() try: for hour in range(start_time.hour, end_time.hour): if hour in self.hour_index: jobs.extend(self.hour_index[hour]) jobs = set(jobs) # Clear second and be care of start minute determine_time = start_time if start_time.second != 0: determine_time = start_time + timedelta(minutes=1) determine_time = datetime.combine(determine_time.date(), time(hour=determine_time.hour, minute=determine_time.minute)) while determine_time < end_time: for job in jobs: if TimeMatcher.matchTimePattern(job.get_time_pattern(), determine_time): task = Task(job.get_jobid(), uuid.uuid1().hex, determine_time, job.getCommandToExcute(), job.get_retry(), job, job.get_depend()) t = TaskRunner((determine_time - datetime.now()).seconds + 1, task, self._status, self) t.daemon = True t.start() task.save() tasks.append(task) self._status.add_waiting_task(task, t) determine_time = determine_time + timedelta(minutes=1) self._lock.release() except Exception, e: self._lock.release() raise e;
async def ping_check(self, data): try: cmd = ["ping", data["ip"]] r = await asyncio.create_subprocess_exec( *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, ) stdout, stderr = await r.communicate() success = ["Reply", "bytes", "time", "TTL"] if stdout: output = stdout.decode("utf-8", errors="ignore") if all(x in output for x in success): status = "passing" else: status = "failing" elif stderr: status = "failing" output = stderr.decode("utf-8", errors="ignore") payload = { "status": status, "more_info": output, } self.logger.debug(payload) resp = requests.patch( f"{self.astor.server}/api/v1/{data['id']}/checkrunner/", json.dumps(payload), headers=self.headers, timeout=15, ) if ( status == "failing" and data["assigned_task"] and data["assigned_task"]["enabled"] ): from taskrunner import TaskRunner task = TaskRunner( task_pk=data["assigned_task"]["id"], log_level=self.log_level, log_to=self.log_to, ) await task.run_while_in_event_loop() return status except Exception as e: self.logger.debug(e) return "failing"
async def disk_check(self, data, exists=True): try: disk = psutil.disk_usage(data["disk"]) except Exception: exists = False self.logger.error(f"Disk {data['disk']} does not exist") if exists: percent_used = round(disk.percent) total = bytes2human(disk.total) free = bytes2human(disk.free) if (100 - percent_used) < data["threshold"]: status = "failing" else: status = "passing" more_info = f"Total: {total}B, Free: {free}B" else: status = "failing" more_info = f"Disk {data['disk']} does not exist" payload = { "status": status, "more_info": more_info, } self.logger.debug(payload) resp = requests.patch( f"{self.astor.server}/api/v1/{data['id']}/checkrunner/", json.dumps(payload), headers=self.headers, timeout=15, ) if ( status == "failing" and data["assigned_task"] and data["assigned_task"]["enabled"] ): from taskrunner import TaskRunner task = TaskRunner( task_pk=data["assigned_task"]["id"], log_level=self.log_level, log_to=self.log_to, ) await task.run_while_in_event_loop() return status
def spawnTask(self, jobid, start_time, end_time): ''' Spawn thread based on one Job's timer @param jobid:int @param start_time:datetime ,start time of time section @param end_time:datetime ,end time of time section ''' tasks = [] self._lock.acquire() try: job = Job.getByJobID(jobid) if not job: raise ValueError("Job(id:%d) is not exsited or not enabled." % (jobid)) # Clear second and be care of start minute determine_time = start_time if start_time.second != 0: determine_time = start_time + timedelta(minutes=1) determine_time = datetime.combine(determine_time.date(), time(hour=determine_time.hour, minute=determine_time.minute)) while determine_time < end_time: if TimeMatcher.matchTimePattern(job.get_time_pattern(), determine_time): task = Task(job.get_jobid(), uuid.uuid1().hex, determine_time, job.getCommandToExcute(), job.get_retry(), job, job.get_depend()) t = TaskRunner((determine_time - datetime.now()).seconds + 1, task, self._status, self) t.daemon = True t.start() task.save() tasks.append(task) self._status.add_waiting_task(task, t) determine_time = determine_time + timedelta(minutes=1) self._lock.release() except Exception, e: self._lock.release() raise e;
# so that people can have different configs for different builds # prepare the environment file config_env = config.asDict() config_env.update(os.environ) if myconfig: config_env.update(myconfig.asDict()) tasks = Task([ Job("pre-flight", "./scripts/pre-flight.sh", env=config_env), Job("win_build", "./scripts/build-windows.sh", env=config_env), Job("lin_build", "./scripts/build-linux.sh", env=config_env), Job("mac_build", "./scripts/build-mac.sh", env=config_env), ]) print "Build getting started at: ", time.ctime() # Run the first task, which will create the docs and sources tarballs tr = TaskRunner(tasks) rc = tr.run() if rc == 0 and config.delete_temps == "yes": shutil.rmtree(config.WX_TEMP_DIR) # cleanup the DAILY_BUILD file if config.KIND == "daily": os.unlink("DAILY_BUILD") print "Build finished at: ", time.ctime() sys.exit(0)
import sys from taskrunner import Task, TaskRunner if __name__ == '__main__': # setup taskrunner taskRunner = TaskRunner( enableHelp = True, defaultTask = Task('default', 'qs version && printf \"\n\" && qs help'), versionTask = Task(['v', 'version'], 'echo \"QuickScript Version 1.3\nCreated by Nicholas Ramsay \"', 'Shows the current QuickScript version.') ) # get tasks from file and add to taskRunner with open('tasks.csv', 'r') as f: for l in f.readlines(0): segments = l.split(', ') taskRunner.AddTask(Task(segments[0].split(' '), segments[1], segments[2])) # Run the command line argument task try: if len(sys.argv[1:]) < 1: taskRunner.RunTask('') else: for arg in sys.argv[1:]: taskRunner.RunTask(arg) except Exception as e: print('\033[91m' + str(e) + '\033[0m\n') taskRunner.RunTask('help')
async def script_check(self, data): try: script_path = data["script"]["filepath"] shell = data["script"]["shell"] timeout = data["timeout"] script_filename = data["script"]["filename"] if shell == "python": cmd = [ self.salt_call, "win_agent.run_python_script", script_filename, f"timeout={timeout}", ] try: script_type = data["script"]["script_type"] except KeyError: pass else: cmd.append(f"script_type={script_type}") else: cmd = [ self.salt_call, "cmd.script", script_path, f"shell={shell}", f"timeout={timeout}", ] self.logger.debug(cmd) start = perf_counter() proc = await asyncio.create_subprocess_exec( *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) proc_timeout = int(timeout) + 2 try: proc_stdout, proc_stderr = await asyncio.wait_for( proc.communicate(), proc_timeout ) except asyncio.TimeoutError: try: proc.terminate() except: pass self.logger.debug(f"Script check timed out after {timeout} seconds") proc_stdout, proc_stderr = False, False stdout = "" stderr = f"Script timed out after {timeout} seconds" retcode = 98 stop = perf_counter() if proc_stdout: resp = json.loads(proc_stdout.decode("utf-8", errors="ignore")) retcode = resp["local"]["retcode"] stdout = resp["local"]["stdout"] stderr = resp["local"]["stderr"] elif proc_stderr: retcode = 99 stdout = "" stderr = proc_stderr.decode("utf-8", errors="ignore") if retcode != 0: status = "failing" else: status = "passing" payload = { "stdout": stdout, "stderr": stderr, "status": status, "retcode": retcode, "execution_time": "{:.4f}".format(round(stop - start)), } self.logger.debug(payload) resp = requests.patch( f"{self.astor.server}/api/v1/{data['id']}/checkrunner/", json.dumps(payload), headers=self.headers, timeout=15, ) if ( status == "failing" and data["assigned_task"] and data["assigned_task"]["enabled"] ): from taskrunner import TaskRunner task = TaskRunner( task_pk=data["assigned_task"]["id"], log_level=self.log_level, log_to=self.log_to, ) await task.run_while_in_event_loop() return status except Exception as e: self.logger.debug(e) return "failing"
async def event_log_check(self, data): try: log = [] api_log_name = data["log_name"] api_event_id = int(data["event_id"]) api_event_type = data["event_type"] api_fail_when = data["fail_when"] api_search_last_days = int(data["search_last_days"]) try: api_event_id_is_wildcard = data["event_id_is_wildcard"] except KeyError: api_event_id_is_wildcard = False if api_search_last_days != 0: start_time = dt.datetime.now() - dt.timedelta(days=api_search_last_days) flags = ( win32evtlog.EVENTLOG_BACKWARDS_READ | win32evtlog.EVENTLOG_SEQUENTIAL_READ ) status_dict = { win32con.EVENTLOG_AUDIT_FAILURE: "AUDIT_FAILURE", win32con.EVENTLOG_AUDIT_SUCCESS: "AUDIT_SUCCESS", win32con.EVENTLOG_INFORMATION_TYPE: "INFO", win32con.EVENTLOG_WARNING_TYPE: "WARNING", win32con.EVENTLOG_ERROR_TYPE: "ERROR", 0: "INFO", } hand = win32evtlog.OpenEventLog("localhost", api_log_name) total = win32evtlog.GetNumberOfEventLogRecords(hand) uid = 0 done = False while 1: events = win32evtlog.ReadEventLog(hand, flags, 0) for ev_obj in events: uid += 1 # return once total number of events reach or we'll be stuck in an infinite loop if uid >= total: done = True break the_time = ev_obj.TimeGenerated.Format() time_obj = dt.datetime.strptime(the_time, "%c") if api_search_last_days != 0: if time_obj < start_time: done = True break computer = str(ev_obj.ComputerName) src = str(ev_obj.SourceName) evt_type = str(status_dict[ev_obj.EventType]) evt_id = str(winerror.HRESULT_CODE(ev_obj.EventID)) evt_category = str(ev_obj.EventCategory) record = str(ev_obj.RecordNumber) msg = ( str(win32evtlogutil.SafeFormatMessage(ev_obj, api_log_name)) .replace("<", "") .replace(">", "") ) event_dict = { "computer": computer, "source": src, "eventType": evt_type, "eventID": evt_id, "eventCategory": evt_category, "message": msg, "time": the_time, "record": record, "uid": uid, } if api_event_id_is_wildcard and evt_type == api_event_type: log.append(event_dict) elif int(evt_id) == api_event_id and evt_type == api_event_type: log.append(event_dict) if done: break win32evtlog.CloseEventLog(hand) if api_fail_when == "contains": if log: status = "failing" more_info = {"log": log} else: status = "passing" more_info = {"log": []} elif api_fail_when == "not_contains": if log: status = "passing" more_info = {"log": log} else: status = "failing" more_info = {"log": []} else: status = "failing" more_info = {"log": []} payload = { "status": status, "extra_details": more_info, } self.logger.debug(payload) resp = requests.patch( f"{self.astor.server}/api/v1/{data['id']}/checkrunner/", json.dumps(payload), headers=self.headers, timeout=15, ) if ( status == "failing" and data["assigned_task"] and data["assigned_task"]["enabled"] ): from taskrunner import TaskRunner task = TaskRunner( task_pk=data["assigned_task"]["id"], log_level=self.log_level, log_to=self.log_to, ) await task.run_while_in_event_loop() return status except Exception as e: self.logger.debug(e) return "failing"
async def win_service_check(self, data, exists=True): try: services = self.get_services() try: service = list( filter(lambda x: x["name"] == data["svc_name"], services) )[0] except IndexError: exists = False self.logger.error(f"Service {data['svc_name']} does not exist") if exists: service_status = service["status"] if service_status == "running": status = "passing" elif ( service_status == "start_pending" and data["pass_if_start_pending"] ): status = "passing" else: status = "failing" if data["restart_if_stopped"]: ret = self.salt_call_ret_bool( cmd="service.restart", args=[data["svc_name"]], timeout=60, ) # wait a bit to give service time to start before checking status again await asyncio.sleep(10) reloaded = self.get_services() stat = list( filter(lambda x: x["name"] == data["svc_name"], reloaded) )[0]["status"] if stat == "running": status = "passing" elif stat == "start_pending" and data["pass_if_start_pending"]: status = "passing" else: status = "failing" service_status = stat else: status = "failing" payload = { "status": status, "more_info": f"Status {service_status.upper()}" if exists else f"Service {data['svc_name']} does not exist", } self.logger.debug(payload) resp = requests.patch( f"{self.astor.server}/api/v1/{data['id']}/checkrunner/", json.dumps(payload), headers=self.headers, timeout=15, ) if ( status == "failing" and data["assigned_task"] and data["assigned_task"]["enabled"] ): from taskrunner import TaskRunner task = TaskRunner( task_pk=data["assigned_task"]["id"], log_level=self.log_level, log_to=self.log_to, ) await task.run_while_in_event_loop() return status except Exception as e: self.logger.debug(e) return "failing"
from taskrunner import TaskRunner tr = TaskRunner('cluster') tr.run_build() print(tr.run_job())
def main(): parser = argparse.ArgumentParser(description="Tactical RMM Agent") parser.add_argument("-m", action="store", dest="mode", type=str) parser.add_argument("-p", action="store", dest="taskpk", type=int) parser.add_argument("--api", action="store", dest="api_url", type=str) parser.add_argument("--client-id", action="store", dest="client_id", type=int) parser.add_argument("--site-id", action="store", dest="site_id", type=int) parser.add_argument( "--desc", action="store", dest="agent_desc", type=str, default="changeme" ) parser.add_argument( "--agent-type", action="store", dest="agent_type", type=str, default="server", choices=["server", "workstation"], ) parser.add_argument("--auth", action="store", dest="auth_token", type=str) args = parser.parse_args() if args.mode == "install": import sys import threading if len(sys.argv) != 15: parser.print_help() raise SystemExit() from installer import Installer installer = Installer( api_url=args.api_url, client_id=args.client_id, site_id=args.site_id, agent_desc=args.agent_desc, agent_type=args.agent_type, auth_token=args.auth_token, ) t = threading.Thread(target=installer.install, daemon=True) t.start() t.join() elif args.mode == "winagentsvc": from winagentsvc import WinAgentSvc agent = WinAgentSvc() agent.run() elif args.mode == "checkrunner": from checkrunner import CheckRunner agent = CheckRunner() agent.run_forever() elif args.mode == "runchecks": from checkrunner import CheckRunner agent = CheckRunner() agent.run() elif args.mode == "winupdater": from winupdater import WinUpdater agent = WinUpdater() agent.install_all() elif args.mode == "patchscan": from winupdater import WinUpdater agent = WinUpdater() agent.trigger_patch_scan() elif args.mode == "taskrunner": from taskrunner import TaskRunner agent = TaskRunner(task_pk=args.taskpk) agent.run() elif args.mode == "updatesalt": from agent import WindowsAgent agent = WindowsAgent() agent.fix_salt(by_time=False) agent.update_salt() elif args.mode == "fixsalt": from agent import WindowsAgent agent = WindowsAgent() agent.fix_salt() elif args.mode == "fixmesh": from agent import WindowsAgent agent = WindowsAgent() agent.fix_mesh() elif args.mode == "cleanup": from agent import WindowsAgent agent = WindowsAgent() agent.fix_salt(by_time=False) agent.cleanup() else: import win32gui from agent import show_agent_status window = win32gui.GetForegroundWindow() if window == 0: # called from cli with no interactive desktop show_agent_status(window=None, gui=False) else: show_agent_status(window=window, gui=True)
def main(): parser = argparse.ArgumentParser(description="Tactical RMM Agent") parser.add_argument("-m", action="store", dest="mode", type=str) parser.add_argument("-p", action="store", dest="taskpk", type=int) parser.add_argument("--api", action="store", dest="api_url", type=str) parser.add_argument("--client-id", action="store", dest="client_id", type=int) parser.add_argument("--site-id", action="store", dest="site_id", type=int) parser.add_argument( "--desc", action="store", dest="agent_desc", type=str, default=socket.gethostname(), ) parser.add_argument( "--agent-type", action="store", dest="agent_type", type=str, default="server", choices=["server", "workstation"], ) parser.add_argument( "-l", "--log", action="store", dest="log_level", type=str, default="INFO", choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"], ) parser.add_argument( "--logto", action="store", dest="log_to", type=str, default="file", choices=["file", "stdout"], ) parser.add_argument( "--power", action="store", dest="power", type=int, default=0, choices=[0, 1], ) parser.add_argument( "--rdp", action="store", dest="rdp", type=int, default=0, choices=[0, 1], ) parser.add_argument( "--ping", action="store", dest="ping", type=int, default=0, choices=[0, 1], ) parser.add_argument("--auth", action="store", dest="auth_token", type=str) parser.add_argument("--version", action="store_true") parser.add_argument( "--local-salt", action="store", dest="local_salt", type=str, help= r'The full path to the salt-minion executable e.g. "C:\\temp\\salt-minion-setup.exe"', ) parser.add_argument( "--local-mesh", action="store", dest="local_mesh", type=str, help= r'The full path to the Mesh Agent executable e.g. "C:\\temp\\meshagent.exe"', ) args = parser.parse_args() if args.version: try: with open( os.path.join("C:\\Program Files\\TacticalAgent", "VERSION")) as f: ver = f.read().strip() print(ver) except Exception as e: print(f"Error getting version: {e}") elif args.mode == "install": if (not args.api_url or not args.client_id or not args.site_id or not args.auth_token): parser.print_help() sys.exit(1) if args.local_salt: if not os.path.exists(args.local_salt): parser.print_help() sys.stdout.flush() print(f"\nError: {args.local_salt} does not exist\n", flush=True) sys.exit(1) if not os.path.isfile(args.local_salt): parser.print_help() sys.stdout.flush() print( f"\nError: {args.local_salt} must be a file, not a folder.", flush=True, ) print( r'Make sure to use double backslashes for file paths, and double quotes e.g. "C:\\temp\\salt-minion-setup.exe"', flush=True, ) print("", flush=True) sys.exit(1) if args.local_mesh: if not os.path.exists(args.local_mesh): parser.print_help() sys.stdout.flush() print(f"\nError: {args.local_mesh} does not exist\n", flush=True) sys.exit(1) if not os.path.isfile(args.local_mesh): parser.print_help() sys.stdout.flush() print( f"\nError: {args.local_mesh} must be a file, not a folder.", flush=True, ) print( r'Make sure to use double backslashes for file paths, and double quotes e.g. "C:\\temp\\meshagent.exe"', flush=True, ) print("", flush=True) sys.exit(1) from installer import Installer installer = Installer( api_url=args.api_url, client_id=args.client_id, site_id=args.site_id, agent_desc=args.agent_desc, agent_type=args.agent_type, power=args.power, rdp=args.rdp, ping=args.ping, auth_token=args.auth_token, log_level=args.log_level, local_salt=args.local_salt, local_mesh=args.local_mesh, ) installer.install() elif args.mode == "winagentsvc": from winagentsvc import WinAgentSvc agent = WinAgentSvc(log_level=args.log_level, log_to=args.log_to) agent.run() elif args.mode == "checkrunner": from checkrunner import CheckRunner agent = CheckRunner(log_level=args.log_level, log_to=args.log_to) agent.run_forever() elif args.mode == "runchecks": from checkrunner import CheckRunner agent = CheckRunner(log_level=args.log_level, log_to=args.log_to) agent.run() elif args.mode == "winupdater": from winupdater import WinUpdater agent = WinUpdater(log_level=args.log_level, log_to=args.log_to) agent.install_all() elif args.mode == "patchscan": from winupdater import WinUpdater agent = WinUpdater(log_level=args.log_level, log_to=args.log_to) agent.trigger_patch_scan() elif args.mode == "taskrunner": from taskrunner import TaskRunner agent = TaskRunner(task_pk=args.taskpk, log_level=args.log_level, log_to=args.log_to) agent.run() elif args.mode == "updatesalt": from agent import WindowsAgent agent = WindowsAgent(log_level=args.log_level, log_to=args.log_to) agent.fix_salt(by_time=False) agent.update_salt() elif args.mode == "fixsalt": from agent import WindowsAgent agent = WindowsAgent(log_level=args.log_level, log_to=args.log_to) agent.fix_salt() elif args.mode == "fixmesh": from agent import WindowsAgent agent = WindowsAgent(log_level=args.log_level, log_to=args.log_to) agent.fix_mesh() elif args.mode == "cleanup": from agent import WindowsAgent agent = WindowsAgent(log_level=args.log_level, log_to=args.log_to) agent.fix_salt(by_time=False) agent.cleanup() elif args.mode == "recoversalt": from agent import WindowsAgent agent = WindowsAgent(log_level=args.log_level, log_to=args.log_to) agent.recover_salt() elif args.mode == "recovermesh": from agent import WindowsAgent agent = WindowsAgent(log_level=args.log_level, log_to=args.log_to) agent.recover_mesh() else: import win32gui from agent import show_agent_status window = win32gui.GetForegroundWindow() if window == 0: # called from cli with no interactive desktop show_agent_status(window=None, gui=False) else: show_agent_status(window=window, gui=True)