Release 0.6.1

This commit is contained in:
wh1te909
2021-04-16 07:46:42 +00:00
23 changed files with 202 additions and 130 deletions

View File

@@ -166,7 +166,7 @@ class Agent(BaseAuditModel):
@property @property
def checks(self): def checks(self):
total, passing, failing = 0, 0, 0 total, passing, failing, warning, info = 0, 0, 0, 0, 0
if self.agentchecks.exists(): # type: ignore if self.agentchecks.exists(): # type: ignore
for i in self.agentchecks.all(): # type: ignore for i in self.agentchecks.all(): # type: ignore
@@ -174,13 +174,20 @@ class Agent(BaseAuditModel):
if i.status == "passing": if i.status == "passing":
passing += 1 passing += 1
elif i.status == "failing": elif i.status == "failing":
failing += 1 if i.alert_severity == "error":
failing += 1
elif i.alert_severity == "warning":
warning += 1
elif i.alert_severity == "info":
info += 1
ret = { ret = {
"total": total, "total": total,
"passing": passing, "passing": passing,
"failing": failing, "failing": failing,
"has_failing_checks": failing > 0, "warning": warning,
"info": info,
"has_failing_checks": failing > 0 or warning > 0,
} }
return ret return ret

View File

@@ -1,7 +1,6 @@
import asyncio import asyncio
import datetime as dt import datetime as dt
import random import random
import requests
import urllib.parse import urllib.parse
from time import sleep from time import sleep
from typing import Union from typing import Union
@@ -21,21 +20,9 @@ from tacticalrmm.utils import run_nats_api_cmd
logger.configure(**settings.LOG_CONFIG) logger.configure(**settings.LOG_CONFIG)
def _get_exegen_url() -> str:
urls: list[str] = settings.EXE_GEN_URLS
for url in urls:
try:
r = requests.get(url, timeout=10)
except:
continue
if r.status_code == 200:
return url
return random.choice(urls)
def agent_update(pk: int, codesigntoken: str = None) -> str: def agent_update(pk: int, codesigntoken: str = None) -> str:
from agents.utils import get_exegen_url
agent = Agent.objects.get(pk=pk) agent = Agent.objects.get(pk=pk)
if pyver.parse(agent.version) <= pyver.parse("1.3.0"): if pyver.parse(agent.version) <= pyver.parse("1.3.0"):
@@ -52,7 +39,7 @@ def agent_update(pk: int, codesigntoken: str = None) -> str:
inno = agent.win_inno_exe inno = agent.win_inno_exe
if codesigntoken is not None and pyver.parse(version) >= pyver.parse("1.5.0"): if codesigntoken is not None and pyver.parse(version) >= pyver.parse("1.5.0"):
base_url = _get_exegen_url() + "/api/v1/winagents/?" base_url = get_exegen_url() + "/api/v1/winagents/?"
params = {"version": version, "arch": agent.arch, "token": codesigntoken} params = {"version": version, "arch": agent.arch, "token": codesigntoken}
url = base_url + urllib.parse.urlencode(params) url = base_url + urllib.parse.urlencode(params)
else: else:

View File

@@ -914,7 +914,7 @@ class TestAgentTasks(TacticalTestCase):
self.authenticate() self.authenticate()
self.setup_coresettings() self.setup_coresettings()
@patch("agents.tasks._get_exegen_url") @patch("agents.utils.get_exegen_url")
@patch("agents.models.Agent.nats_cmd") @patch("agents.models.Agent.nats_cmd")
def test_agent_update(self, nats_cmd, get_exe): def test_agent_update(self, nats_cmd, get_exe):
from agents.tasks import agent_update from agents.tasks import agent_update

View File

@@ -0,0 +1,37 @@
import random
import requests
import urllib.parse
from django.conf import settings
def get_exegen_url() -> str:
urls: list[str] = settings.EXE_GEN_URLS
for url in urls:
try:
r = requests.get(url, timeout=10)
except:
continue
if r.status_code == 200:
return url
return random.choice(urls)
def get_winagent_url(arch: str) -> str:
from core.models import CodeSignToken
try:
codetoken = CodeSignToken.objects.first().token
base_url = get_exegen_url() + "/api/v1/winagents/?"
params = {
"version": settings.LATEST_AGENT_VER,
"arch": arch,
"token": codetoken,
}
dl_url = base_url + urllib.parse.urlencode(params)
except:
dl_url = settings.DL_64 if arch == "64" else settings.DL_32
return dl_url

View File

@@ -353,6 +353,7 @@ class Reboot(APIView):
@api_view(["POST"]) @api_view(["POST"])
def install_agent(request): def install_agent(request):
from agents.utils import get_winagent_url
from knox.models import AuthToken from knox.models import AuthToken
client_id = request.data["client"] client_id = request.data["client"]
@@ -375,7 +376,7 @@ def install_agent(request):
inno = ( inno = (
f"winagent-v{version}.exe" if arch == "64" else f"winagent-v{version}-x86.exe" f"winagent-v{version}.exe" if arch == "64" else f"winagent-v{version}-x86.exe"
) )
download_url = settings.DL_64 if arch == "64" else settings.DL_32 download_url = get_winagent_url(arch)
_, token = AuthToken.objects.create( _, token = AuthToken.objects.create(
user=request.user, expiry=dt.timedelta(hours=request.data["expires"]) user=request.user, expiry=dt.timedelta(hours=request.data["expires"])

View File

@@ -86,7 +86,10 @@ class GetUpdateDeleteCheck(APIView):
check = get_object_or_404(Check, pk=pk) check = get_object_or_404(Check, pk=pk)
# remove fields that should not be changed when editing a check from the frontend # remove fields that should not be changed when editing a check from the frontend
if "check_alert" not in request.data.keys(): if (
"check_alert" not in request.data.keys()
and "check_reset" not in request.data.keys()
):
[request.data.pop(i) for i in check.non_editable_fields] [request.data.pop(i) for i in check.non_editable_fields]
# set event id to 0 if wildcard because it needs to be an integer field for db # set event id to 0 if wildcard because it needs to be an integer field for db
@@ -108,6 +111,11 @@ class GetUpdateDeleteCheck(APIView):
if check.policy: if check.policy:
update_policy_check_fields_task(checkpk=pk) update_policy_check_fields_task(checkpk=pk)
# resolve any alerts that are open
if "check_reset" in request.data.keys():
if obj.alert.filter(resolved=False).exists():
obj.alert.get(resolved=False).resolve()
return Response(f"{obj.readable_desc} was edited!") return Response(f"{obj.readable_desc} was edited!")
def delete(self, request, pk): def delete(self, request, pk):

View File

@@ -86,16 +86,24 @@ class Client(BaseAuditModel):
.prefetch_related("agentchecks") .prefetch_related("agentchecks")
) )
failing = 0 data = {"error": False, "warning": False}
for agent in agents: for agent in agents:
if agent.checks["has_failing_checks"]: if agent.checks["has_failing_checks"]:
failing += 1
if agent.checks["warning"]:
data["warning"] = True
if agent.checks["failing"]:
data["error"] = True
break
if agent.overdue_email_alert or agent.overdue_text_alert: if agent.overdue_email_alert or agent.overdue_text_alert:
if agent.status == "overdue": if agent.status == "overdue":
failing += 1 data["error"] = True
break
return failing > 0 return data
@staticmethod @staticmethod
def serialize(client): def serialize(client):
@@ -184,16 +192,24 @@ class Site(BaseAuditModel):
.prefetch_related("agentchecks") .prefetch_related("agentchecks")
) )
failing = 0 data = {"error": False, "warning": False}
for agent in agents: for agent in agents:
if agent.checks["has_failing_checks"]: if agent.checks["has_failing_checks"]:
failing += 1 if agent.checks["warning"]:
data["warning"] = True
if agent.checks["failing"]:
data["error"] = True
break
if agent.overdue_email_alert or agent.overdue_text_alert: if agent.overdue_email_alert or agent.overdue_text_alert:
if agent.status == "overdue": if agent.status == "overdue":
failing += 1 data["error"] = True
break
return failing > 0 return data
@staticmethod @staticmethod
def serialize(site): def serialize(site):

View File

@@ -95,7 +95,6 @@ class SiteTreeSerializer(ModelSerializer):
class Meta: class Meta:
model = Site model = Site
fields = "__all__" fields = "__all__"
ordering = ("failing_checks",)
class ClientTreeSerializer(ModelSerializer): class ClientTreeSerializer(ModelSerializer):
@@ -106,7 +105,6 @@ class ClientTreeSerializer(ModelSerializer):
class Meta: class Meta:
model = Client model = Client
fields = "__all__" fields = "__all__"
ordering = ("failing_checks",)
class DeploymentSerializer(ModelSerializer): class DeploymentSerializer(ModelSerializer):

View File

@@ -51,7 +51,7 @@
"name": "Disk - Cleanup C: drive", "name": "Disk - Cleanup C: drive",
"description": "Cleans the C: drive's Window Temperary files, Windows SoftwareDistribution folder, the local users Temperary folder, IIS logs (if applicable) and empties the recycling bin. All deleted files will go into a log transcript in $env:TEMP. By default this script leaves files that are newer than 7 days old however this variable can be edited.", "description": "Cleans the C: drive's Window Temperary files, Windows SoftwareDistribution folder, the local users Temperary folder, IIS logs (if applicable) and empties the recycling bin. All deleted files will go into a log transcript in $env:TEMP. By default this script leaves files that are newer than 7 days old however this variable can be edited.",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Other" "category": "TRMM (Win):Maintenance"
}, },
{ {
"guid": "2f28e8c1-ae0f-4b46-a826-f513974526a3", "guid": "2f28e8c1-ae0f-4b46-a826-f513974526a3",
@@ -75,7 +75,7 @@
"guid": "3c46290b-85db-4cd2-93a2-943c8c93b3b1", "guid": "3c46290b-85db-4cd2-93a2-943c8c93b3b1",
"filename": "Speedtest.py", "filename": "Speedtest.py",
"submittedBy": "https://github.com/wh1te909", "submittedBy": "https://github.com/wh1te909",
"name": "Network - Speed Test", "name": "Speed Test - Python",
"description": "Runs a Speed Test using Python", "description": "Runs a Speed Test using Python",
"shell": "python", "shell": "python",
"category": "TRMM (Win):Network" "category": "TRMM (Win):Network"
@@ -102,7 +102,7 @@
"guid": "2ea35fa2-c227-4d17-a40e-4d39f252e27a", "guid": "2ea35fa2-c227-4d17-a40e-4d39f252e27a",
"filename": "Win_Bitlocker_Create_Status_Report.ps1", "filename": "Win_Bitlocker_Create_Status_Report.ps1",
"submittedBy": "https://github.com/ThatsNASt", "submittedBy": "https://github.com/ThatsNASt",
"name": "Create Bitlocker Status Report", "name": "Bitlocker - Create Status Report",
"description": "Creates a Bitlocker status report.", "description": "Creates a Bitlocker status report.",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Storage" "category": "TRMM (Win):Storage"
@@ -228,7 +228,7 @@
"guid": "24f19ead-fdfe-46b4-9dcb-4cd0e12a3940", "guid": "24f19ead-fdfe-46b4-9dcb-4cd0e12a3940",
"filename": "Win_Speedtest.ps1", "filename": "Win_Speedtest.ps1",
"submittedBy": "https://github.com/dinger1986", "submittedBy": "https://github.com/dinger1986",
"name": "Speed Test Powershell", "name": "Speed Test - Powershell",
"description": "Speed Test with Powershell(win 10 or server2016+)", "description": "Speed Test with Powershell(win 10 or server2016+)",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Network" "category": "TRMM (Win):Network"
@@ -327,7 +327,7 @@
"guid": "5615aa90-0272-427b-8acf-0ca019612501", "guid": "5615aa90-0272-427b-8acf-0ca019612501",
"filename": "Win_Chocolatey_Update_Installed.bat", "filename": "Win_Chocolatey_Update_Installed.bat",
"submittedBy": "https://github.com/silversword411", "submittedBy": "https://github.com/silversword411",
"name": "Chocolatey Update Installed Apps", "name": "Update Installed Apps",
"description": "Update all apps that were installed using Chocolatey.", "description": "Update all apps that were installed using Chocolatey.",
"shell": "cmd", "shell": "cmd",
"category": "TRMM (Win):3rd Party Software>Chocolatey" "category": "TRMM (Win):3rd Party Software>Chocolatey"
@@ -394,7 +394,7 @@
"name": "Updates - Finish and restart", "name": "Updates - Finish and restart",
"description": "Finish installing Windows updates and restart PC", "description": "Finish installing Windows updates and restart PC",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Other" "category": "TRMM (Win):Updates"
}, },
{ {
"guid": "63f89be0-a9c9-4c61-9b55-bce0b28b90b2", "guid": "63f89be0-a9c9-4c61-9b55-bce0b28b90b2",
@@ -403,7 +403,7 @@
"name": "Updates - Finish and Shutdown", "name": "Updates - Finish and Shutdown",
"description": "Finish installing Windows updates and shutdown PC", "description": "Finish installing Windows updates and shutdown PC",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Other" "category": "TRMM (Win):Updates"
}, },
{ {
"guid": "e09895d5-ca13-44a2-a38c-6e77c740f0e8", "guid": "e09895d5-ca13-44a2-a38c-6e77c740f0e8",
@@ -460,6 +460,16 @@
"category": "TRMM (Win):Network", "category": "TRMM (Win):Network",
"default_timeout": "90" "default_timeout": "90"
}, },
{
"guid": "abe78170-7cf9-435b-9666-c5ef6c11a106",
"filename": "Win_Network_IPv6_Disable.ps1",
"submittedBy": "https://github.com/silversword411",
"name": "Network IPv6 - Disable",
"description": "Disable IPv6 on all adapters",
"shell": "powershell",
"category": "TRMM (Win):Network",
"default_timeout": "90"
},
{ {
"guid": "6ce5682a-49db-4c0b-9417-609cf905ac43", "guid": "6ce5682a-49db-4c0b-9417-609cf905ac43",
"filename": "Win_Win10_Change_Key_and_Activate.ps1", "filename": "Win_Win10_Change_Key_and_Activate.ps1",
@@ -563,9 +573,9 @@
}, },
{ {
"guid": "77da9c87-5a7a-4ba1-bdde-3eeb3b01d62d", "guid": "77da9c87-5a7a-4ba1-bdde-3eeb3b01d62d",
"filename": "Win_Set_Network_To_Private.ps1", "filename": "Win_Network_Set_To_Private.ps1",
"submittedBy": "https://github.com/tremor021", "submittedBy": "https://github.com/tremor021",
"name": "Set Network To Private", "name": "Network Category - Set Network To Private",
"description": "Sets current network type to Private", "description": "Sets current network type to Private",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Network" "category": "TRMM (Win):Network"
@@ -574,7 +584,7 @@
"guid": "768f42d5-7b45-45ed-8233-254ae537aaa2", "guid": "768f42d5-7b45-45ed-8233-254ae537aaa2",
"filename": "Win_TaskScheduler_Add_Task.ps1", "filename": "Win_TaskScheduler_Add_Task.ps1",
"submittedBy": "https://github.com/tremor021", "submittedBy": "https://github.com/tremor021",
"name": "Add task to TaskScheduler", "name": "Task Scheduler - Add a task",
"description": "Add a task to Task Scheduler, needs editing", "description": "Add a task to Task Scheduler, needs editing",
"shell": "powershell", "shell": "powershell",
"category": "TRMM (Win):Other" "category": "TRMM (Win):Other"

View File

@@ -15,14 +15,14 @@ EXE_DIR = os.path.join(BASE_DIR, "tacticalrmm/private/exe")
AUTH_USER_MODEL = "accounts.User" AUTH_USER_MODEL = "accounts.User"
# latest release # latest release
TRMM_VERSION = "0.6.0" TRMM_VERSION = "0.6.1"
# bump this version everytime vue code is changed # bump this version everytime vue code is changed
# to alert user they need to manually refresh their browser # to alert user they need to manually refresh their browser
APP_VER = "0.0.129" APP_VER = "0.0.130"
# https://github.com/wh1te909/rmmagent # https://github.com/wh1te909/rmmagent
LATEST_AGENT_VER = "1.5.0" LATEST_AGENT_VER = "1.5.1"
MESH_VER = "0.7.93" MESH_VER = "0.7.93"

View File

@@ -52,7 +52,7 @@ def generate_winagent_exe(
file_name: str, file_name: str,
) -> Union[Response, FileResponse]: ) -> Union[Response, FileResponse]:
from agents.tasks import _get_exegen_url from agents.utils import get_exegen_url
inno = ( inno = (
f"winagent-v{settings.LATEST_AGENT_VER}.exe" f"winagent-v{settings.LATEST_AGENT_VER}.exe"
@@ -62,7 +62,7 @@ def generate_winagent_exe(
try: try:
codetoken = CodeSignToken.objects.first().token codetoken = CodeSignToken.objects.first().token
base_url = _get_exegen_url() + "/api/v1/winagents/?" base_url = get_exegen_url() + "/api/v1/winagents/?"
params = { params = {
"version": settings.LATEST_AGENT_VER, "version": settings.LATEST_AGENT_VER,
"arch": arch, "arch": arch,

View File

@@ -4,7 +4,7 @@ A backup script is provided for quick and easy way to backup all settings into o
Download the backup script: Download the backup script:
```bash ```bash
wget https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh wget -N https://raw.githubusercontent.com/wh1te909/tacticalrmm/master/backup.sh
``` ```
From the Web UI, click **Tools > Server Maintenance** From the Web UI, click **Tools > Server Maintenance**

View File

@@ -0,0 +1,8 @@
#CVE-2020-16898 | Windows TCP/IP Remote Code Execution Vulnerability
#https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-16898
#Disable IPv6 on All Adapers
Disable-NetAdapterBinding -Name "*" -ComponentID ms_tcpip6
#Confirm That all NIC's no longer have IPv6 Enabled
(Get-NetAdapterBinding -Name '*' -ComponentID ms_tcpip6).Enabled

View File

@@ -303,17 +303,18 @@
/> />
</q-td> </q-td>
<q-td key="checks-status" :props="props"> <q-td key="checks-status" :props="props">
<q-icon v-if="props.row.maintenance_mode" name="fas fa-check-double" size="1.2em" color="warning"> <q-icon v-if="props.row.maintenance_mode" name="construction" size="1.2em" color="green">
<q-tooltip>Maintenance Mode Enabled</q-tooltip> <q-tooltip>Maintenance Mode Enabled</q-tooltip>
</q-icon> </q-icon>
<q-icon <q-icon v-else-if="props.row.checks.failing > 0" name="fas fa-check-double" size="1.2em" color="negative">
v-else-if="props.row.checks.has_failing_checks"
name="fas fa-check-double"
size="1.2em"
color="negative"
>
<q-tooltip>Checks failing</q-tooltip> <q-tooltip>Checks failing</q-tooltip>
</q-icon> </q-icon>
<q-icon v-else-if="props.row.checks.warning > 0" name="fas fa-check-double" size="1.2em" color="warning">
<q-tooltip>Checks warning</q-tooltip>
</q-icon>
<q-icon v-else-if="props.row.checks.info > 0" name="fas fa-check-double" size="1.2em" color="info">
<q-tooltip>Checks info</q-tooltip>
</q-icon>
<q-icon v-else name="fas fa-check-double" size="1.2em" color="positive"> <q-icon v-else name="fas fa-check-double" size="1.2em" color="positive">
<q-tooltip>Checks passing</q-tooltip> <q-tooltip>Checks passing</q-tooltip>
</q-icon> </q-icon>

View File

@@ -18,6 +18,7 @@
<template v-else> <template v-else>
<q-table <q-table
dense dense
:table-class="{ 'table-bgcolor': !$q.dark.isActive, 'table-bgcolor-dark': $q.dark.isActive }"
class="tabs-tbl-sticky" class="tabs-tbl-sticky"
:style="{ 'max-height': tabsTableHeight }" :style="{ 'max-height': tabsTableHeight }"
:data="tasks" :data="tasks"

View File

@@ -124,6 +124,13 @@
<q-item-section>Delete</q-item-section> <q-item-section>Delete</q-item-section>
</q-item> </q-item>
<q-separator></q-separator> <q-separator></q-separator>
<q-item clickable v-close-popup @click="resetCheck(props.row.id)">
<q-item-section side>
<q-icon name="info" />
</q-item-section>
<q-item-section>Reset Check Status</q-item-section>
</q-item>
<q-separator></q-separator>
<q-item clickable v-close-popup> <q-item clickable v-close-popup>
<q-item-section>Close</q-item-section> <q-item-section>Close</q-item-section>
</q-item> </q-item>
@@ -431,7 +438,25 @@ export default {
}); });
}); });
}, },
resetCheck(check) {
const data = {
check_reset: true,
status: "passing",
};
axios
.patch(`/checks/${check}/check/`, data)
.then(r => {
this.$emit("refreshEdit");
this.$store.dispatch("loadChecks", this.selectedAgentPk);
this.notifySuccess("The check was reset");
})
.catch(e => {
this.notifyError("There was an issue resetting the check");
});
},
onRefresh(id) { onRefresh(id) {
this.$emit("refreshEdit");
this.$store.dispatch("loadChecks", id); this.$store.dispatch("loadChecks", id);
this.$store.dispatch("loadAutomatedTasks", id); this.$store.dispatch("loadAutomatedTasks", id);
}, },

View File

@@ -283,10 +283,18 @@
<q-tooltip> Batch </q-tooltip> <q-tooltip> Batch </q-tooltip>
</q-icon> </q-icon>
</q-td> </q-td>
<!-- name -->
<q-td>{{ props.row.name }}</q-td> <q-td>{{ props.row.name }}</q-td>
<!-- args -->
<q-td> <q-td>
<span v-if="props.row.args.length > 0">{{ props.row.args }}</span> <span v-if="props.row.args.length > 0">
{{ truncateText(props.row.args.toString()) }}
<q-tooltip v-if="props.row.args.toString().length >= 60" content-style="font-size: 12px">
{{ props.row.args }}
</q-tooltip>
</span>
</q-td> </q-td>
<q-td>{{ props.row.category }}</q-td> <q-td>{{ props.row.category }}</q-td>
<q-td> <q-td>
{{ truncateText(props.row.description) }} {{ truncateText(props.row.description) }}
@@ -363,6 +371,7 @@ export default {
label: "Default Args", label: "Default Args",
field: "args", field: "args",
align: "left", align: "left",
sortable: true,
}, },
{ {
name: "category", name: "category",
@@ -516,11 +525,9 @@ export default {
}, },
categories() { categories() {
let list = []; let list = [];
this.scripts.forEach(script => { this.visibleScripts.forEach(script => {
if (!!script.category && !list.includes(script.category)) { if (!!script.category && !list.includes(script.category)) {
if (script.category !== "Community") { list.push(script.category);
list.push(script.category);
}
} }
}); });
return list; return list;
@@ -538,8 +545,7 @@ export default {
let scriptsTemp = Object.assign([], this.visibleScripts); let scriptsTemp = Object.assign([], this.visibleScripts);
let categoriesTemp = Object.assign([], this.categories); let categoriesTemp = Object.assign([], this.categories);
// add Community and Unassigned values and categories array // add Unassigned category
if (this.showCommunityScripts) categoriesTemp.push("Community");
categoriesTemp.push("Unassigned"); categoriesTemp.push("Unassigned");
const sorted = categoriesTemp.sort(); const sorted = categoriesTemp.sort();

View File

@@ -25,7 +25,7 @@
<SummaryTab /> <SummaryTab />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="checks" class="q-pb-xs q-pt-none"> <q-tab-panel name="checks" class="q-pb-xs q-pt-none">
<ChecksTab /> <ChecksTab @refreshEdit="$emit('refreshEdit')" />
</q-tab-panel> </q-tab-panel>
<q-tab-panel name="tasks" class="q-pb-xs q-pt-none"> <q-tab-panel name="tasks" class="q-pb-xs q-pt-none">
<AutomatedTasksTab /> <AutomatedTasksTab />

View File

@@ -4,7 +4,7 @@
<q-btn class="q-mr-sm" dense flat push icon="refresh" @click="refreshSummary" /> <q-btn class="q-mr-sm" dense flat push icon="refresh" @click="refreshSummary" />
<span> <span>
<b>{{ summary.hostname }}</b> <b>{{ summary.hostname }}</b>
<span v-if="summary.maintenance_mode"> &bull; <q-badge color="warning"> Maintenance Mode </q-badge> </span> <span v-if="summary.maintenance_mode"> &bull; <q-badge color="green"> Maintenance Mode </q-badge> </span>
&bull; {{ summary.operating_system }} &bull; Agent v{{ summary.version }} &bull; {{ summary.operating_system }} &bull; Agent v{{ summary.version }}
</span> </span>
<hr /> <hr />
@@ -72,6 +72,14 @@
<q-avatar size="lg" square icon="cancel" color="red" text-color="white" /> <q-avatar size="lg" square icon="cancel" color="red" text-color="white" />
<small>{{ summary.checks.failing }} checks failing</small> <small>{{ summary.checks.failing }} checks failing</small>
</q-chip> </q-chip>
<q-chip v-if="summary.checks.warning" square size="lg">
<q-avatar size="lg" square icon="warning" color="warning" text-color="white" />
<small>{{ summary.checks.warning }} checks warning</small>
</q-chip>
<q-chip v-if="summary.checks.info" square size="lg">
<q-avatar size="lg" square icon="info" color="info" text-color="white" />
<small>{{ summary.checks.info }} checks info</small>
</q-chip>
<span v-if="awaitingSync(summary.checks.total, summary.checks.passing, summary.checks.failing)" <span v-if="awaitingSync(summary.checks.total, summary.checks.passing, summary.checks.failing)"
>{{ summary.checks.total }} checks awaiting first synchronization</span >{{ summary.checks.total }} checks awaiting first synchronization</span
> >

View File

@@ -93,7 +93,7 @@
<div class="col-3">Day of month to run:</div> <div class="col-3">Day of month to run:</div>
<div class="col-4"></div> <div class="col-4"></div>
<q-select <q-select
:disable="winupdatepolicy.run_time_frequency === 'inherit'" v-show="winupdatepolicy.run_time_frequency !== 'inherit'"
dense dense
class="col-5" class="col-5"
outlined outlined
@@ -103,11 +103,10 @@
map-options map-options
/> />
</q-card-section> </q-card-section>
<q-card-section class="row"> <q-card-section class="row" v-show="winupdatepolicy.run_time_frequency !== 'inherit'">
<div class="col-3">Scheduled Time:</div> <div class="col-3">Scheduled Time:</div>
<div class="col-4"></div> <div class="col-4"></div>
<q-select <q-select
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
dense dense
class="col-5" class="col-5"
outlined outlined
@@ -118,51 +117,17 @@
/> />
</q-card-section> </q-card-section>
<q-card-section <q-card-section
v-if="winupdatepolicy.run_time_frequency === 'daily' || winupdatepolicy.run_time_frequency === 'inherit'" v-if="winupdatepolicy.run_time_frequency === 'daily'"
v-show="winupdatepolicy.run_time_frequency !== 'inherit'"
> >
<div class="q-gutter-sm"> <div class="q-gutter-sm">
<q-checkbox <q-checkbox v-model="winupdatepolicy.run_time_days" :val="1" label="Monday" />
:disable="winupdatepolicy.run_time_frequency === 'inherit'" <q-checkbox v-model="winupdatepolicy.run_time_days" :val="2" label="Tuesday" />
v-model="winupdatepolicy.run_time_days" <q-checkbox v-model="winupdatepolicy.run_time_days" :val="3" label="Wednesday" />
:val="1" <q-checkbox v-model="winupdatepolicy.run_time_days" :val="4" label="Thursday" />
label="Monday" <q-checkbox v-model="winupdatepolicy.run_time_days" :val="5" label="Friday" />
/> <q-checkbox v-model="winupdatepolicy.run_time_days" :val="6" label="Saturday" />
<q-checkbox <q-checkbox v-model="winupdatepolicy.run_time_days" :val="0" label="Sunday" />
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="2"
label="Tuesday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="3"
label="Wednesday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="4"
label="Thursday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="5"
label="Friday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="6"
label="Saturday"
/>
<q-checkbox
:disable="winupdatepolicy.run_time_frequency === 'inherit'"
v-model="winupdatepolicy.run_time_days"
:val="0"
label="Sunday"
/>
</div> </div>
</q-card-section> </q-card-section>
<!-- Reboot After Installation --> <!-- Reboot After Installation -->
@@ -189,20 +154,15 @@
<q-checkbox v-model="winupdatepolicy.reprocess_failed_inherit" label="Inherit failed patch settings" /> <q-checkbox v-model="winupdatepolicy.reprocess_failed_inherit" label="Inherit failed patch settings" />
</div> </div>
</q-card-section> </q-card-section>
<q-card-section class="row"> <q-card-section class="row" v-show="!winupdatepolicy.reprocess_failed_inherit">
<div class="col-5"> <div class="col-5">
<q-checkbox <q-checkbox v-model="winupdatepolicy.reprocess_failed" label="Reprocess failed patches" />
:disable="winupdatepolicy.reprocess_failed_inherit"
v-model="winupdatepolicy.reprocess_failed"
label="Reprocess failed patches"
/>
</div> </div>
<div class="col-3"> <div class="col-3">
<q-input <q-input
dense dense
v-model.number="winupdatepolicy.reprocess_failed_times" v-model.number="winupdatepolicy.reprocess_failed_times"
:disable="winupdatepolicy.reprocess_failed_inherit"
type="number" type="number"
filled filled
label="Times" label="Times"
@@ -210,11 +170,7 @@
/> />
</div> </div>
<div class="col-3"></div> <div class="col-3"></div>
<q-checkbox <q-checkbox v-model="winupdatepolicy.email_if_fail" label="Send an email when patch installation fails" />
v-model="winupdatepolicy.email_if_fail"
:disable="winupdatepolicy.reprocess_failed_inherit"
label="Send an email when patch installation fails"
/>
</q-card-section> </q-card-section>
<q-card-actions align="left" v-if="policy"> <q-card-actions align="left" v-if="policy">
<q-btn label="Submit" color="primary" @click="submit" /> <q-btn label="Submit" color="primary" @click="submit" />
@@ -249,6 +205,7 @@ export default {
run_time_hour: 3, run_time_hour: 3,
run_time_frequency: "daily", run_time_frequency: "daily",
run_time_days: [], run_time_days: [],
run_time_day: 1,
reboot_after_install: "never", reboot_after_install: "never",
reprocess_failed_inherit: false, reprocess_failed_inherit: false,
reprocess_failed: false, reprocess_failed: false,

View File

@@ -256,8 +256,9 @@ export default function () {
alert_template: site.alert_template alert_template: site.alert_template
} }
if (site.maintenance_mode) { siteNode["color"] = "warning" } if (site.maintenance_mode) { siteNode["color"] = "green" }
else if (site.failing_checks) { siteNode["color"] = "negative" } else if (site.failing_checks.error) { siteNode["color"] = "negative" }
else if (site.failing_checks.warning) { siteNode["color"] = "warning" }
childSites.push(siteNode); childSites.push(siteNode);
} }
@@ -274,8 +275,9 @@ export default function () {
children: childSites children: childSites
} }
if (client.maintenance_mode) clientNode["color"] = "warning" if (client.maintenance_mode) clientNode["color"] = "green"
else if (client.failing_checks) clientNode["color"] = "negative" else if (client.failing_checks.error) { clientNode["color"] = "negative" }
else if (client.failing_checks.warning) { clientNode["color"] = "warning" }
output.push(clientNode); output.push(clientNode);
} }

View File

@@ -343,7 +343,7 @@
<q-avatar color="primary" text-color="white" size="30px" icon="drag_indicator" /> <q-avatar color="primary" text-color="white" size="30px" icon="drag_indicator" />
</template> </template>
<template v-slot:after> <template v-slot:after>
<SubTableTabs /> <SubTableTabs @refreshEdit="refreshEntireSite" />
</template> </template>
</q-splitter> </q-splitter>
</template> </template>
@@ -727,10 +727,10 @@ export default {
let data = { let data = {
id: node.id, id: node.id,
type: node.raw.split("|")[0], type: node.raw.split("|")[0],
action: node.color === "warning" ? false : true, action: node.color === "green" ? false : true,
}; };
const text = node.color === "warning" ? "Maintenance mode was disabled" : "Maintenance mode was enabled"; const text = node.color === "green" ? "Maintenance mode was disabled" : "Maintenance mode was enabled";
this.$store this.$store
.dispatch("toggleMaintenanceMode", data) .dispatch("toggleMaintenanceMode", data)
.then(response => { .then(response => {
@@ -742,7 +742,7 @@ export default {
}); });
}, },
menuMaintenanceText(node) { menuMaintenanceText(node) {
return node.color === "warning" ? "Disable Maintenance Mode" : "Enable Maintenance Mode"; return node.color === "green" ? "Disable Maintenance Mode" : "Enable Maintenance Mode";
}, },
clearFilter() { clearFilter() {
this.filterTextLength = 0; this.filterTextLength = 0;