Feature: Display detailed device hardware audit information

The device Details tab now displays processor, memory, disk and network adapter information collected at the previous enrolment
This commit is contained in:
Gary Sharp
2020-11-29 16:45:10 +11:00
parent f1d27732c7
commit f82b47dd46
6 changed files with 1331 additions and 143 deletions
@@ -0,0 +1,90 @@
using Disco.Models.ClientServices.EnrolmentInformation;
namespace Disco.Services
{
public static class MeasurementUnitExtensions
{
private const ulong kilobyte = 1024ul;
private const ulong megabyte = kilobyte * 1024ul;
private const ulong gigabyte = megabyte * 1024ul;
private const ulong terabyte = gigabyte * 1024ul;
private const ulong maxbit = 9223372036854775807ul;
private const ulong kilobit = 1000ul;
private const ulong megabit = kilobit * 1000ul;
private const ulong gigabit = megabit * 1000ul;
private const uint gigahertz = 1000u;
public static string ByteSizeToFriendly(ulong bytes)
{
if (bytes >= terabyte)
{
return $"{(double)bytes / terabyte:N2} TB";
}
if (bytes >= gigabyte)
{
return $"{(double)bytes / gigabyte:N2} GB";
}
if (bytes >= megabyte)
{
return $"{(double)bytes / megabyte:N2} MB";
}
if (bytes >= kilobyte)
{
return $"{(double)bytes / kilobyte:N2} KB";
}
return $"{bytes:N0} B";
}
public static string SpeedPacketBitsToFriendly(ulong speed)
{
if (speed == maxbit || speed == 0)
return "Unknown";
if (speed >= gigabit)
{
if (speed % gigabit == 0)
return $"{(double)speed / gigabit:N0} Gbps";
else
return $"{(double)speed / gigabit:N2} Gbps";
}
if (speed >= megabit)
{
if (speed % megabit == 0)
return $"{(double)speed / megabit:N0} Mbps";
else
return $"{(double)speed / megabit:N2} Mbps";
}
if (speed >= kilobit)
{
if (speed % kilobit == 0)
return $"{(double)speed / kilobit:N0} Kbps";
else
return $"{(double)speed / kilobit:N2} Kbps";
}
return $"{speed:N0} Bps";
}
public static string SpeedMegahertzToFriendly(uint speed)
{
if (speed >= gigahertz)
{
if (speed % gigahertz == 0)
return $"{(double)speed / gigahertz:N0} GHz";
else
return $"{(double)speed / gigahertz:N2} GHz";
}
return $"{speed:N0} MHz";
}
public static string SizeFriendly(this DiskDrive diskDrive) => ByteSizeToFriendly(diskDrive.Size);
public static string SizeFriendly(this DiskDrivePartition partition) => ByteSizeToFriendly(partition.Size);
public static string SizeFriendly(this DiskLogical disk) => ByteSizeToFriendly(disk.Size);
public static string FreeSpaceFriendly(this DiskLogical disk) => ByteSizeToFriendly(disk.FreeSpace);
public static string SpeedFriendly(this NetworkAdapter networkAdapter) => SpeedPacketBitsToFriendly(networkAdapter.Speed);
public static string CapacityFriendly(this PhysicalMemory physicalMemory) => ByteSizeToFriendly(physicalMemory.Capacity);
public static string MaxClockSpeedFriendly(this Processor processor) => SpeedMegahertzToFriendly(processor.MaxClockSpeed);
}
}
+41 -12
View File
@@ -7,7 +7,7 @@
background-color: #fff;
}
.tableData > tbody > tr:nth-child(odd) > td {
background-color: #fbfbfb;
background-color: hsl(0, 0%, 98.5%);
}
.tableData > thead > tr > th,
.tableData > tbody > tr > th {
@@ -15,7 +15,7 @@
border: solid 1px #f4f4f4;
}
.tableData > tbody > tr:hover > td {
background-color: #f9f9f9;
background-color: hsl(0, 0%, 97.5%);
}
.tableData > tfoot > tr > th,
.tableData > tfoot > tr > td {
@@ -201,7 +201,7 @@
}
#Device_Show_Policies_Profile_Actions_Update_Dialog ul li:nth-child(odd),
#Device_Show_Policies_Batch_Actions_Update_Dialog ul li:nth-child(odd) {
background-color: #fbfbfb;
background-color: hsl(0, 0%, 98.5%);
}
#Device_Show_Policies_Profile_Actions_Update_Dialog ul li.selected,
#Device_Show_Policies_Batch_Actions_Update_Dialog ul li.selected {
@@ -235,7 +235,7 @@
background-color: #fff;
}
#DeviceDetailTab-DetailsContainer > table > tbody > tr:nth-child(odd) > td {
background-color: #fbfbfb;
background-color: hsl(0, 0%, 98.5%);
}
#DeviceDetailTab-DetailsContainer > table > thead > tr > th,
#DeviceDetailTab-DetailsContainer > table > tbody > tr > th {
@@ -243,7 +243,7 @@
border: solid 1px #f4f4f4;
}
#DeviceDetailTab-DetailsContainer > table > tbody > tr:hover > td {
background-color: #f9f9f9;
background-color: hsl(0, 0%, 97.5%);
}
#DeviceDetailTab-DetailsContainer > table > tfoot > tr > th,
#DeviceDetailTab-DetailsContainer > table > tfoot > tr > td {
@@ -253,9 +253,38 @@
width: 150px;
}
#DeviceDetailTab-DetailsContainer > table > tbody > tr > th,
#DeviceDetailTab-DetailsContainer > table > tbody > tr > td {
#DeviceDetailTab-DetailsContainer > table > tbody > tr > td.pad {
padding: 10px 6px;
}
#DeviceDetailTab-DetailsContainer .device_detail_disk_drives .partition {
position: relative;
height: 40px;
}
#DeviceDetailTab-DetailsContainer .device_detail_disk_drives .partition > span {
position: absolute;
display: block;
height: 40px;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: hidden;
text-overflow: ellipsis;
white-space: nowrap;
background-color: #f4f4f4;
padding: 2px;
line-height: 18px;
border: 1px solid #cacaca;
}
#DeviceDetailTab-DetailsContainer .device_detail_disk_drives .partition > span .details {
position: relative;
}
#DeviceDetailTab-DetailsContainer .device_detail_disk_drives .partition > span .freespace {
position: absolute;
top: 0;
height: 40px;
display: block;
background-color: #fff;
background: repeating-linear-gradient(45deg, #f4f4f4, #f4f4f4 10px, #fff 10px, #fff 20px);
}
#deviceShowResources #AttachmentsContainer {
padding: 0;
}
@@ -319,7 +348,7 @@
border: 1px solid #ccc;
}
#deviceShowResources #Attachments div.attachmentOutput > a:hover span.remove {
opacity: .5;
opacity: 0.5;
}
#deviceShowResources #Attachments div.attachmentOutput > a span.remove {
font-size: 1.4em;
@@ -345,7 +374,7 @@
cursor: pointer;
float: right;
border: 1px solid #fff;
padding: .5em;
padding: 0.5em;
}
#deviceShowResources #Attachments div.attachmentInput span.action:hover {
color: #335A87;
@@ -366,14 +395,14 @@
display: none;
}
#Devices_Export #Devices_Export_Fields #Devices_Export_Fields_Defaults {
font-size: .75em;
font-size: 0.75em;
}
#Devices_Export #Devices_Export_Fields th {
font-size: 1.05em;
}
#Devices_Export #Devices_Export_Fields th span {
margin-top: 4px;
font-size: .8em;
font-size: 0.8em;
}
#Devices_Export_Download_Dialog {
padding-top: 20px;
@@ -489,7 +518,7 @@
background-color: #e7f9d5;
}
#Devices_Import_Review #Devices_Import_Review_Navigation ul li.actionUnchanged {
background-color: #fbfbfb;
background-color: hsl(0, 0%, 98.5%);
}
#Devices_Import_Review #Devices_Import_Review_TableContainer {
margin: 18px 0;
@@ -518,7 +547,7 @@
content: "\f068";
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionUnchanged td {
background-color: #fbfbfb;
background-color: hsl(0, 0%, 98.5%);
}
#Devices_Import_Review #Devices_Import_Review_TableContainer table > tbody tr.actionUnchanged td:nth-child(n+3) {
color: #ccc;
+34 -1
View File
@@ -215,11 +215,44 @@
width: 150px;
}
& > th, & > td {
& > th, & > td.pad {
padding: 10px 6px;
}
}
}
.device_detail_disk_drives .partition {
position: relative;
height: 40px;
& > span {
position: absolute;
display: block;
height: 40px;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: hidden;
text-overflow: ellipsis;
white-space: nowrap;
background-color: #f4f4f4;
padding: 2px;
line-height: 18px;
border: 1px solid #cacaca;
.details {
position: relative;
}
.freespace {
position: absolute;
top: 0;
height: 40px;
display: block;
background-color: #fff;
background: repeating-linear-gradient(45deg, #f4f4f4, #f4f4f4 10px, #fff 10px, #fff 20px);
}
}
}
}
File diff suppressed because one or more lines are too long
@@ -8,70 +8,291 @@
{
Html.BundleDeferred("~/ClientScripts/Modules/Disco-PropertyChangeHelpers");
}
var processors = Model.Device.DeviceDetails.Processors();
var physicalMemory = Model.Device.DeviceDetails.PhysicalMemory();
var diskDrives = Model.Device.DeviceDetails.DiskDrives();
var networkAdapters = Model.Device.DeviceDetails.NetworkAdapters();
var lanMacAddress = Model.Device.DeviceDetails.LanMacAddress();
var wlanMacAddress = Model.Device.DeviceDetails.WLanMacAddress();
}
<div id="DeviceDetailTab-Details" class="DevicePart">
<div id="DeviceDetailTab-DetailsContainer">
<table class="tableData">
<tbody>
<tr>
<th>LAN MAC Address</th>
<td class="code">@(Model.Device.DeviceDetails.LanMacAddress() ?? "Unknown")</td>
</tr>
<tr>
<th>WLAN MAC Address</th>
<td class="code">@(Model.Device.DeviceDetails.WLanMacAddress() ?? "Unknown")</td>
</tr>
@if (processors != null)
{
<tr>
<th>Processors</th>
<td class="device_detail_processors">
<table class="tableData">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
<th>Architecture</th>
<th>Max Clock Speed</th>
<th>Cores</th>
<th>Logical Processors</th>
</tr>
</thead>
<tbody>
@foreach (var processor in processors)
{
<tr>
<td>@processor.Name</td>
<td>@processor.Description</td>
<td>@processor.Architecture</td>
<td>@processor.MaxClockSpeedFriendly()</td>
<td>@processor.NumberOfCores.ToString("N0")</td>
<td>@processor.NumberOfLogicalProcessors.ToString("N0")</td>
</tr>
}
</tbody>
</table>
</td>
</tr>
}
@if (physicalMemory != null)
{
<tr class="device_detail_memory">
<th>Memory</th>
<td>
<table class="tableData">
<thead>
<tr>
<th>Location</th>
<th>Manufacturer</th>
<th>Part Number</th>
<th>Serial Number</th>
<th>Capacity</th>
<th>Clock Speed</th>
</tr>
</thead>
<tbody>
@foreach (var memory in physicalMemory)
{
<tr>
<td>@memory.DeviceLocator</td>
<td>@memory.Manufacturer</td>
<td>@memory.PartNumber</td>
<td>@memory.SerialNumber</td>
<td>@memory.CapacityFriendly()</td>
<td>@memory.ConfiguredClockSpeed</td>
</tr>
}
</tbody>
</table>
</td>
</tr>
}
@if (networkAdapters != null)
{
<tr class="device_detail_network_adapters">
<th>Network Adapters</th>
<td>
<table class="tableData">
<thead>
<tr>
<th>Connection</th>
<th>Manufacturer</th>
<th>Product</th>
<th>Speed</th>
<th>MAC Address</th>
</tr>
</thead>
<tbody>
@foreach (var adapter in networkAdapters)
{
<tr>
<td>
@(adapter.NetConnectionID ?? "N/A") @if (adapter.IsWlanAdapter)
{<i class="fa fa-wifi"></i>}
</td>
<td>@adapter.Manufacturer</td>
<td>@adapter.ProductName</td>
<td>@adapter.SpeedFriendly()</td>
<td>@adapter.MACAddress</td>
</tr>
}
</tbody>
</table>
</td>
</tr>
}
else
{
if (lanMacAddress != null)
{
<tr>
<th>LAN MAC Address</th>
<td class="pad code">@(lanMacAddress)</td>
</tr>
}
if (wlanMacAddress != null)
{
<tr>
<th>WLAN MAC Address</th>
<td class="pad code">@(wlanMacAddress)</td>
</tr>
}
}
@if (diskDrives != null)
{
<tr class="device_detail_disk_drives">
<th>Disk Drives</th>
<td>
<table class="tableData">
<thead>
<tr>
<th>Manufacturer</th>
<th>Model</th>
<th>Serial Number</th>
<th>Firmware</th>
<th>Type</th>
<th>Interface</th>
<th>Size</th>
</tr>
</thead>
<tbody>
@foreach (var disk in diskDrives)
{
<tr>
<td>@disk.Manufacturer</td>
<td>@disk.Model</td>
<td>@disk.SerialNumber</td>
<td>@disk.FirmwareRevision</td>
<td>@disk.MediaType</td>
<td>@disk.InterfaceType</td>
<td>@disk.SizeFriendly()</td>
</tr>
if (disk.Partitions != null)
{
// calculate stretched offsets
var minPartitionSize = (double)disk.Size * 0.1;
var diskSizeAdjusted = disk.Partitions.Sum(p => Math.Max(minPartitionSize, p.Size));
var diskAdjustedOffet = (double)0;
<tr>
<td colspan="7">
<div class="partition">
@foreach (var partition in disk.Partitions)
{
var logicalDisk = partition.LogicalDisk;
var offsetPercentage = Math.Round(((partition.StartingOffset + diskAdjustedOffet) / diskSizeAdjusted) * 100, 3);
var widthPercentage = Math.Round((Math.Max(minPartitionSize, partition.Size) / diskSizeAdjusted) * 100, 3);
var freeSpacePercentage = 0d;
if (partition.Size < minPartitionSize)
{
diskAdjustedOffet += minPartitionSize - partition.Size;
}
var partitionTitle = partition.Type;
var tags = new List<string>();
if (partition.BootPartition)
{
tags.Add("Boot");
}
if (partition.PrimaryParition)
{
tags.Add("Primary");
}
if (logicalDisk != null)
{
partitionTitle = string.Format("{0} {1}", logicalDisk.DeviceID, logicalDisk.VolumeName);
tags.Add(logicalDisk.SizeFriendly());
tags.Add(logicalDisk.FreeSpaceFriendly() + " Free");
tags.Add(logicalDisk.FileSystem);
tags.Add(logicalDisk.DriveType);
freeSpacePercentage = Math.Round(((double)logicalDisk.FreeSpace / logicalDisk.Size) * 100, 3);
}
else
{
tags.Add(partition.SizeFriendly());
}
var tag = string.Join(" | ", tags);
<span title="@(partitionTitle) &#xA@(tag)" style="left: @(offsetPercentage)%; width: @(widthPercentage)%">
@if (freeSpacePercentage > 0.5)
{
<span class="freespace" style="left: @(100 - freeSpacePercentage)%; width: @(freeSpacePercentage)%"></span>
}
<span class="details">
<strong>@partitionTitle</strong>
<br />
@tag
</span>
</span>
}
</div>
</td>
</tr>
}
}
</tbody>
</table>
</td>
</tr>
}
<tr>
<th>AC Adapter</th>
<td>@if (canConfig)
<td class="pad">
@if (canConfig)
{
@Html.TextBox("DeviceDetail_ACAdapter", Model.Device.DeviceDetails.ACAdapter()) @AjaxHelpers.AjaxSave() @AjaxHelpers.AjaxLoader()
<script>
@Html.TextBox("DeviceDetail_ACAdapter", Model.Device.DeviceDetails.ACAdapter()) @AjaxHelpers.AjaxSave() @AjaxHelpers.AjaxLoader()
<script>
$(function () {
document.DiscoFunctions.PropertyChangeHelper($('#DeviceDetail_ACAdapter'), 'Unknown', '@Url.Action(MVC.API.Device.UpdateDetailACAdapter(Model.Device.SerialNumber, null))', 'DetailACAdapter');
});
</script>
</script>
}
else
{
@(Model.Device.DeviceDetails.ACAdapter() ?? "Unknown")
@(Model.Device.DeviceDetails.ACAdapter() ?? "Unknown")
}
</td>
</tr>
<tr>
<th>Battery</th>
<td>@if (canConfig)
<td class="pad">
@if (canConfig)
{
@Html.TextBox("DeviceDetail_Battery", Model.Device.DeviceDetails.Battery()) @AjaxHelpers.AjaxSave() @AjaxHelpers.AjaxLoader()
<script>
@Html.TextBox("DeviceDetail_Battery", Model.Device.DeviceDetails.Battery()) @AjaxHelpers.AjaxSave() @AjaxHelpers.AjaxLoader()
<script>
$(function () {
document.DiscoFunctions.PropertyChangeHelper($('#DeviceDetail_Battery'), 'Unknown', '@Url.Action(MVC.API.Device.UpdateDetailBattery(Model.Device.SerialNumber, null))', 'DetailBattery');
});
</script>
</script>
}
else
{
@(Model.Device.DeviceDetails.Battery() ?? "Unknown")
@(Model.Device.DeviceDetails.Battery() ?? "Unknown")
}
</td>
</tr>
<tr>
<th>Keyboard</th>
<td>@if (canConfig)
<td class="pad">
@if (canConfig)
{
@Html.TextBox("DeviceDetail_Keyboard", Model.Device.DeviceDetails.Keyboard()) @AjaxHelpers.AjaxSave() @AjaxHelpers.AjaxLoader()
<script>
@Html.TextBox("DeviceDetail_Keyboard", Model.Device.DeviceDetails.Keyboard()) @AjaxHelpers.AjaxSave() @AjaxHelpers.AjaxLoader()
<script>
$(function () {
document.DiscoFunctions.PropertyChangeHelper($('#DeviceDetail_Keyboard'), 'Unknown', '@Url.Action(MVC.API.Device.UpdateDetailKeyboard(Model.Device.SerialNumber, null))', 'DetailKeyboard');
});
</script>
</script>
}
else
{
@(Model.Device.DeviceDetails.Keyboard() ?? "Unknown")
@(Model.Device.DeviceDetails.Keyboard() ?? "Unknown")
}
</td>
</tr>
@if (Model.Device.LastEnrolDate.HasValue)
{
<tr>
<td colspan="2"><em>Details collected @CommonHelpers.FriendlyDate(Model.Device.LastEnrolDate) at time of last enrolment</em></td>
</tr>
}
</tbody>
</table>
</div>
File diff suppressed because it is too large Load Diff