start adding hardware info and summary tab

This commit is contained in:
wh1te909 2020-02-23 11:17:57 +00:00
parent 5dd64946b5
commit 00f6eff10a
8 changed files with 222 additions and 26 deletions

47
_modules/system_info.py Normal file
View File

@ -0,0 +1,47 @@
from __future__ import absolute_import
import wmi
class SystemDetail:
def __init__(self):
self.c = wmi.WMI()
self.make_model = self.c.Win32_ComputerSystemProduct()
self.memory = self.c.Win32_PhysicalMemory()
self.os = self.c.Win32_OperatingSystem()
self.base_board = self.c.Win32_BaseBoard()
self.bios = self.c.Win32_BIOS()
self.disk = self.c.Win32_DiskDrive()
self.network_adapter = self.c.Win32_NetworkAdapter()
self.network_config = self.c.Win32_NetworkAdapterConfiguration()
self.desktop_monitor = self.c.Win32_DesktopMonitor()
self.cpu = self.c.Win32_Processor()
self.usb = self.c.Win32_USBController()
def get_all(self, obj):
ret = []
for i in obj:
tmp = [
{j: getattr(i, j)}
for j in list(i.properties)
if getattr(i, j) is not None
]
ret.append(tmp)
return ret
def system_info():
info = SystemDetail()
return {
"make_model": info.get_all(info.make_model),
"mem": info.get_all(info.memory),
"os": info.get_all(info.os),
"base_board": info.get_all(info.base_board),
"bios": info.get_all(info.bios),
"disk": info.get_all(info.disk),
"network_adapter": info.get_all(info.network_adapter),
"network_config": info.get_all(info.network_config),
"desktop_monitor": info.get_all(info.desktop_monitor),
"cpu": info.get_all(info.cpu),
"usb": info.get_all(info.usb),
}

View File

@ -0,0 +1,19 @@
# Generated by Django 3.0.3 on 2020-02-23 07:23
import django.contrib.postgres.fields.jsonb
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('agents', '0002_auto_20200206_0554'),
]
operations = [
migrations.AddField(
model_name='agent',
name='wmi_detail',
field=django.contrib.postgres.fields.jsonb.JSONField(null=True),
),
]

View File

@ -50,6 +50,7 @@ class Agent(models.Model):
managed_by_wsus = models.BooleanField(default=False)
is_updating = models.BooleanField(default=False)
choco_installed = models.BooleanField(default=False)
wmi_detail = JSONField(null=True)
def __str__(self):
return self.hostname

View File

@ -52,6 +52,7 @@ class AgentSerializer(serializers.ModelSerializer):
"winupdatepolicy",
"salt_id",
"choco_installed",
"wmi_detail",
)

View File

@ -11,6 +11,18 @@ from agents.models import Agent
logger.configure(**settings.LOG_CONFIG)
@app.task
def get_wmi_detail_task(pk):
sleep(30)
agent = Agent.objects.get(pk=pk)
resp = agent.salt_api_cmd(
hostname=agent.salt_id, timeout=30, func="system_info.system_info"
)
agent.wmi_detail = resp.json()["return"][0][agent.salt_id]
agent.save(update_fields=["wmi_detail"])
return "ok"
@app.task
def sync_salt_modules_task(pk):
agent = Agent.objects.get(pk=pk)

View File

@ -38,7 +38,11 @@ from checks.models import (
CpuHistory,
)
from winupdate.models import WinUpdate, WinUpdatePolicy
from agents.tasks import uninstall_agent_task, sync_salt_modules_task
from agents.tasks import (
uninstall_agent_task,
sync_salt_modules_task,
get_wmi_detail_task,
)
from winupdate.tasks import check_for_updates_task
from agents.serializers import AgentHostnameSerializer
from software.tasks import install_chocolatey, get_installed_software
@ -168,6 +172,7 @@ def get_mesh_exe(request):
response["X-Accel-Redirect"] = "/protected/meshagent.exe"
return response
@api_view(["POST"])
@authentication_classes((BasicAuthentication,))
@permission_classes((IsAuthenticated,))
@ -229,10 +234,8 @@ def accept_salt_key(request):
return Response("accepted")
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
return Response(status=status.HTTP_400_BAD_REQUEST)
@api_view(["POST"])
@ -306,7 +309,7 @@ def add(request):
WinUpdatePolicy(agent=agent, run_time_days=[5, 6]).save()
else:
WinUpdatePolicy(agent=agent).save()
return Response({"pk": agent.pk})
else:
return Response("err", status=status.HTTP_400_BAD_REQUEST)
@ -354,6 +357,7 @@ def update(request):
sync_salt_modules_task.delay(agent.pk)
get_installed_software.delay(agent.pk)
get_wmi_detail_task.delay(agent.pk)
if not agent.choco_installed:
install_chocolatey.delay(agent.pk, wait=True)
@ -442,4 +446,3 @@ def hello(request):
cpu.save(update_fields=["cpu_history"])
return Response("ok")

View File

@ -11,10 +11,10 @@
narrow-indicator
no-caps
>
<q-tab name="summary" icon="fas fa-server" size="xs" label="Summary" />
<q-tab name="checks" icon="computer" label="Checks" />
<q-tab name="patches" label="Patches" />
<q-tab name="software" label="Software" />
<q-tab name="summary" icon="fas fa-info-circle" size="xs" label="Summary" />
<q-tab name="checks" icon="fas fa-check-double" label="Checks" />
<q-tab name="patches" icon="system_update" label="Patches" />
<q-tab name="software" icon="fab fa-windows" label="Software" />
</q-tabs>
<q-separator />
<q-tab-panels v-model="subtab" :animated="false">

View File

@ -1,26 +1,139 @@
<template>
<div v-if="Object.keys(summary).length === 0">
No agent selected
</div>
<div v-else>
{{ summary.operating_system }}
<div v-if="Object.keys(summary).length === 0">No agent selected</div>
<div v-else>
<span>
<b>{{ summary.hostname }}</b>
&bull; {{ summary.operating_system }} &bull; Agent v{{ summary.version }}
</span>
<hr />
<div class="row">
<div class="col-4">
<!-- left -->
<q-list dense>
<q-item>
<q-item-section avatar>
<q-icon name="fas fa-desktop" />
</q-item-section>
<q-item-section>{{ makeModel }}</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="fas fa-microchip" />
</q-item-section>
<q-item-section>{{ summary.cpu_info[0].name}}</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="fas fa-memory" />
</q-item-section>
<q-item-section>{{ summary.total_ram}} GB RAM</q-item-section>
</q-item>
<!-- physical disks -->
<q-item v-for="disk in physicalDisks" :key="disk.model">
<q-item-section avatar>
<q-icon name="far fa-hdd" />
</q-item-section>
<q-item-section>{{ disk.model }} {{ disk.size }}GB {{ disk.interfaceType }}</q-item-section>
</q-item>
<q-item>
<q-item-section avatar>
<q-icon name="fas fa-globe-americas" />
</q-item-section>
<q-item-section>Public IP: {{ summary.public_ip}}</q-item-section>
</q-item>
</q-list>
</div>
<div class="col-5"></div>
<!-- right -->
<div class="col-3">
<span class="text-subtitle2 text-bold">Disks</span>
<div v-for="disk in disks" :key="disk.device">
<span>{{ disk.device }} ({{ disk.fstype }})</span>
<q-linear-progress
rounded
size="15px"
:value="disk.percent / 100"
color="green"
class="q-mt-sm"
/>
<span>{{ disk.free }} free of {{ disk.total }}</span>
<hr />
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'SummaryTab',
data() {
return {
}
name: "SummaryTab",
data() {
return {};
},
methods: {
bytesToGB(bytes) {
return Math.round(parseInt(bytes) / 1073741824);
},
computed: {
summary() {
return this.$store.state.agentSummary;
}
validateIPv4(ip) {
const rx = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/;
if (rx.test(ip)) {
return true;
}
return false;
}
}
},
computed: {
summary() {
return this.$store.state.agentSummary;
},
disks() {
const entries = Object.entries(this.summary.disks);
const ret = [];
for (let [k, v] of entries) {
ret.push(v);
}
return ret;
},
makeModel() {
const ret = this.summary.wmi_detail.make_model[0];
return ret.filter(k => k.Version).map(k => k.Version)[0];
},
physicalDisks() {
const ret = this.summary.wmi_detail.disk;
const phys = [];
ret.forEach(disk => {
const model = disk.filter(k => k.Caption).map(k => k.Caption)[0];
const size = disk.filter(k => k.Size).map(k => k.Size)[0];
const interfaceType = disk
.filter(k => k.InterfaceType)
.map(k => k.InterfaceType)[0];
phys.push({
model: model,
size: this.bytesToGB(size),
interfaceType: interfaceType
});
});
return phys;
},
localIPs() {
const ret = this.summary.wmi_detail.network_config;
const ips = [];
ret.forEach(ip => {
const x = ip.filter(k => k.IPAddress).map(k => k.IPAddress)[0];
if (x !== undefined) {
x.forEach(i => {
if (this.validateIPv4(i)) {
ips.push(i);
}
});
}
});
return ips;
}
}
};
</script>