Skip to content

Commit

Permalink
Release 0.2.21
Browse files Browse the repository at this point in the history
  • Loading branch information
wh1te909 committed Jan 5, 2021
2 parents 58b42fa + 90568bb commit 55f3335
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 41 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/.env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
COMPOSE_PROJECT_NAME=trmm

IMAGE_REPO=tacticalrmm
IMAGE_REPO=tacticalrmm/
VERSION=latest

# tactical credentials (Used to login to dashboard)
Expand Down
1 change: 1 addition & 0 deletions .devcontainer/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ services:
APP_HOST: ${APP_HOST}
API_HOST: ${API_HOST}
MESH_HOST: ${MESH_HOST}
MESH_USER: ${MESH_USER}
TRMM_USER: ${TRMM_USER}
TRMM_PASS: ${TRMM_PASS}
depends_on:
Expand Down
4 changes: 2 additions & 2 deletions .devcontainer/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ if [ "$1" = 'tactical-init-dev' ]; then
test -f "${TACTICAL_READY_FILE}" && rm "${TACTICAL_READY_FILE}"

# setup Python virtual env and install dependencies
python -m venv ${VIRTUAL_ENV}
env/bin/pip install --no-cache-dir -r /requirements.txt
python -m venv --copies ${VIRTUAL_ENV}
pip install --no-cache-dir -r /requirements.txt

django_setup

Expand Down
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
**/env
README.md
50 changes: 50 additions & 0 deletions api/tacticalrmm/agents/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from typing import List

from django.conf import settings
from scripts.models import Script

from tacticalrmm.celery import app
from agents.models import Agent, AgentOutage
Expand Down Expand Up @@ -293,3 +294,52 @@ def install_salt_task(pk: int) -> None:
sleep(20)
agent = Agent.objects.get(pk=pk)
asyncio.run(agent.nats_cmd({"func": "installsalt"}, wait=False))


@app.task
def run_script_email_results_task(
agentpk: int, scriptpk: int, nats_timeout: int, nats_data: dict, emails: List[str]
):
agent = Agent.objects.get(pk=agentpk)
script = Script.objects.get(pk=scriptpk)
nats_data["func"] = "runscriptfull"
r = asyncio.run(agent.nats_cmd(nats_data, timeout=nats_timeout))
if r == "timeout":
logger.error(f"{agent.hostname} timed out running script.")
return

CORE = CoreSettings.objects.first()
subject = f"{agent.hostname} {script.name} Results"
exec_time = "{:.4f}".format(r["execution_time"])
body = (
subject
+ f"\nReturn code: {r['retcode']}\nExecution time: {exec_time} seconds\nStdout: {r['stdout']}\nStderr: {r['stderr']}"
)

import smtplib
from email.message import EmailMessage

msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = CORE.smtp_from_email

if emails:
msg["To"] = ", ".join(emails)
else:
msg["To"] = ", ".join(CORE.email_alert_recipients)

msg.set_content(body)

try:
with smtplib.SMTP(CORE.smtp_host, CORE.smtp_port, timeout=20) as server:
if CORE.smtp_requires_auth:
server.ehlo()
server.starttls()
server.login(CORE.smtp_host_user, CORE.smtp_host_password)
server.send_message(msg)
server.quit()
else:
server.send_message(msg)
server.quit()
except Exception as e:
logger.error(e)
21 changes: 20 additions & 1 deletion api/tacticalrmm/agents/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,11 @@
)
from winupdate.serializers import WinUpdatePolicySerializer

from .tasks import uninstall_agent_task, send_agent_update_task
from .tasks import (
uninstall_agent_task,
send_agent_update_task,
run_script_email_results_task,
)
from winupdate.tasks import bulk_check_for_updates_task
from scripts.tasks import handle_bulk_command_task, handle_bulk_script_task

Expand Down Expand Up @@ -738,6 +742,21 @@ def run_script(request):
if output == "wait":
r = asyncio.run(agent.nats_cmd(data, timeout=req_timeout))
return Response(r)
elif output == "email":
if not pyver.parse(agent.version) >= pyver.parse("1.1.12"):
return notify_error("Requires agent version 1.1.12 or greater")

emails = (
[] if request.data["emailmode"] == "default" else request.data["emails"]
)
run_script_email_results_task.delay(
agentpk=agent.pk,
scriptpk=script.pk,
nats_timeout=req_timeout,
nats_data=data,
emails=emails,
)
return Response(f"{script.name} will now be run on {agent.hostname}")
else:
asyncio.run(agent.nats_cmd(data, wait=False))
return Response(f"{script.name} will now be run on {agent.hostname}")
Expand Down
14 changes: 14 additions & 0 deletions api/tacticalrmm/scripts/community_scripts.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,19 @@
"name": "Check BIOS Information",
"description": "Retreives and reports on BIOS make, version, and date .",
"shell": "powershell"
},
{
"filename": "ResetHighPerformancePowerProfiletoDefaults.ps1",
"submittedBy": "https://github.com/azulskyknight",
"name": "Reset High Perf Power Profile",
"description": "Resets monitor, disk, standby, and hibernate timers in the default High Performance power profile to their default values. It also re-indexes the AC and DC power profiles into their default order.",
"shell": "powershell"
},
{
"filename": "SetHighPerformancePowerProfile.ps1",
"submittedBy": "https://github.com/azulskyknight",
"name": "Set High Perf Power Profile",
"description": "Sets the High Performance Power profile to the active power profile. Use this to keep machines from falling asleep.",
"shell": "powershell"
}
]
4 changes: 2 additions & 2 deletions api/tacticalrmm/tacticalrmm/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
AUTH_USER_MODEL = "accounts.User"

# latest release
TRMM_VERSION = "0.2.20"
TRMM_VERSION = "0.2.21"

# bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser
APP_VER = "0.0.101"
APP_VER = "0.0.102"

# https://github.com/wh1te909/salt
LATEST_SALT_VER = "1.1.0"
Expand Down
10 changes: 10 additions & 0 deletions scripts/ResetHighPerformancePowerProfiletoDefaults.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-x -monitor-timeout-ac 15'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-x -disk-timeout-ac 0'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-x -standby-timeout-ac 0'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-x -hibernate-timeout-ac 0'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-setacvalueindex SCHEME_CURRENT 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 0'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-x -monitor-timeout-dc 10'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-x -disk-timeout-dc 0'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-x -standby-timeout-dc 20'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-x -hibernate-timeout-dc 0'
Start-Process -FilePath 'powercfg.exe' -ArgumentList '-setdcvalueindex SCHEME_CURRENT 4f971e89-eebd-4455-a8de-9e59040e7347 5ca83367-6e45-459f-a27b-476b1d01c936 1'
1 change: 1 addition & 0 deletions scripts/SetHighPerformancePowerProfile.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Start-Process -FilePath 'powercfg.exe' -ArgumentList '/setactive 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c'
49 changes: 16 additions & 33 deletions web/src/components/ScriptManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
<q-tree
ref="folderTree"
v-if="!tableView"
style="min-height: 30vh; max-height: 65vh"
style="min-height: 65vh; max-height: 65vh"
class="scroll"
:nodes="tree"
:filter="search"
Expand All @@ -120,6 +120,8 @@
:expanded.sync="expanded"
@update:selected="nodeSelected"
:selected.sync="selected"
no-results-label="No Scripts Found"
no-nodes-label="No Scripts Found"
>
<template v-slot:header-script="props">
<div :class="props.node.id === props.tree.selected ? 'text-primary' : ''">
Expand Down Expand Up @@ -203,7 +205,7 @@
</q-tree>
<q-table
v-if="tableView"
style="min-height: 30vw; max-height: 30vw"
style="min-height: 65vh; max-height: 65vh"
dense
:table-class="{ 'table-bgcolor': !$q.dark.isActive, 'table-bgcolor-dark': $q.dark.isActive }"
class="settings-tbl-sticky scroll"
Expand All @@ -218,6 +220,7 @@
virtual-scroll
flat
:rows-per-page-options="[0]"
no-data-label="No Scripts Found"
>
<template v-slot:header-cell-favorite="props">
<q-th :props="props" auto-width>
Expand Down Expand Up @@ -547,12 +550,18 @@ export default {
return [];
} else {
let nodes = [];
let unassigned = [];
let community = [];
// copy scripts and categories to new array
let scriptsTemp = Object.assign([], this.visibleScripts);
let categoriesTemp = Object.assign([], this.categories);
this.categories.forEach(category => {
// add Community and Unassigned values and categories array
if (this.showCommunityScripts) categoriesTemp.push("Community");
categoriesTemp.push("Unassigned");
const sorted = categoriesTemp.sort();
sorted.forEach(category => {
let temp = {
icon: "folder",
iconColor: "yellow-9",
Expand All @@ -565,41 +574,15 @@ export default {
if (scriptsTemp[i].category === category) {
temp.children.push({ label: scriptsTemp[i].name, header: "script", ...scriptsTemp[i] });
scriptsTemp.splice(i, 1);
} else if (scriptsTemp[i].category === "Community") {
community.push({ label: scriptsTemp[i].name, header: "script", ...scriptsTemp[i] });
scriptsTemp.splice(i, 1);
} else if (!scriptsTemp[i].category) {
unassigned.push({ label: scriptsTemp[i].name, header: "script", ...scriptsTemp[i] });
} else if (category === "Unassigned" && !scriptsTemp[i].category) {
temp.children.push({ label: scriptsTemp[i].name, header: "script", ...scriptsTemp[i] });
scriptsTemp.splice(i, 1);
}
}
nodes.push(temp);
});
if (unassigned.length > 0) {
let temp = {
icon: "folder",
iconColor: "yellow-9",
label: "Unassigned",
id: "Unassigned",
selectable: false,
children: unassigned,
};
nodes.push(temp);
}
if (community.length > 0) {
let temp = {
icon: "folder",
iconColor: "yellow-9",
label: "Community",
id: "Community",
selectable: false,
children: community,
};
nodes.push(temp);
}
return nodes;
}
},
Expand Down
34 changes: 32 additions & 2 deletions web/src/components/modals/agents/RunScript.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,36 @@
</q-card-section>
<q-card-section>
<div class="q-gutter-sm">
<q-radio dense v-model="output" val="wait" label="Wait for Output" />
<q-radio dense v-model="output" val="forget" label="Fire and Forget" />
<q-radio dense v-model="output" val="wait" label="Wait for Output" @input="emails = []" />
<q-radio dense v-model="output" val="forget" label="Fire and Forget" @input="emails = []" />
<q-radio dense v-model="output" val="email" label="Email results" />
</div>
</q-card-section>
<q-card-section v-if="output === 'email'">
<div class="q-gutter-sm">
<q-radio
dense
v-model="emailmode"
val="default"
label="Use email addresses from global settings"
@input="emails = []"
/>
<q-radio dense v-model="emailmode" val="custom" label="Custom emails" />
</div>
</q-card-section>
<q-card-section v-if="emailmode === 'custom' && output === 'email'">
<q-select
label="Email recipients (press Enter after typing each email)"
filled
v-model="emails"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add"
/>
</q-card-section>
<q-card-section>
<q-input
v-model.number="timeout"
Expand Down Expand Up @@ -83,6 +109,8 @@ export default {
ret: null,
output: "wait",
args: [],
emails: [],
emailmode: "default",
};
},
computed: {
Expand Down Expand Up @@ -117,6 +145,8 @@ export default {
scriptPK: this.scriptPK,
output: this.output,
args: this.args,
emails: this.emails,
emailmode: this.emailmode,
};
this.$axios
.post("/agents/runscript/", data)
Expand Down

0 comments on commit 55f3335

Please sign in to comment.