You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
428 lines
25 KiB
428 lines
25 KiB
<div class="flex flex-col flex-auto w-full p-8 xs:p-2">
|
|
|
|
<div class="flex flex-wrap w-full">
|
|
|
|
<div class="flex items-center justify-between w-full my-4 px-4 xs:pr-0">
|
|
<div class="mr-6">
|
|
<h2 class="m-0">Drive Details - {{device | deviceTitle:config.dashboardDisplay}} </h2>
|
|
<div class="text-secondary tracking-tight">Dive into S.M.A.R.T data</div>
|
|
</div>
|
|
<!-- Action buttons -->
|
|
<div class="flex items-center">
|
|
<button class="xs:hidden"
|
|
matTooltip="not yet implemented"
|
|
mat-stroked-button>
|
|
<mat-icon class="icon-size-20"
|
|
[svgIcon]="'save'"></mat-icon>
|
|
<span class="ml-2">Export</span>
|
|
</button>
|
|
<button class="ml-2 xs:hidden"
|
|
(click)="openDialog()"
|
|
mat-stroked-button>
|
|
<mat-icon class="icon-size-20 rotate-90 mirror"
|
|
[svgIcon]="'tune'"></mat-icon>
|
|
<span class="ml-2">Settings</span>
|
|
</button>
|
|
|
|
<!-- Actions menu (visible on xs) -->
|
|
<div class="hidden xs:flex">
|
|
<button [matMenuTriggerFor]="actionsMenu"
|
|
mat-icon-button>
|
|
<mat-icon [svgIcon]="'more_vert'"></mat-icon>
|
|
</button>
|
|
<mat-menu #actionsMenu="matMenu">
|
|
<button mat-menu-item
|
|
matTooltip="not yet implemented">
|
|
<mat-icon class="icon-size-20"
|
|
[svgIcon]="'save'"></mat-icon>
|
|
<span class="ml-2">Export</span>
|
|
</button>
|
|
<button mat-menu-item
|
|
(click)="openDialog()">
|
|
<mat-icon class="icon-size-20 rotate-90 mirror"
|
|
[svgIcon]="'tune'"></mat-icon>
|
|
<span class="ml-2">Settings</span>
|
|
</button>
|
|
</mat-menu>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Card -->
|
|
<div class="flex flex-auto w-1/4 p-4 lt-md:w-full">
|
|
<treo-card class="flex flex-auto p-4 flex-col flex-auto filter-list">
|
|
<div class="flex flex-col grid grid-cols-2">
|
|
<div *ngIf="device" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>
|
|
<span class="inline-flex items-center font-bold text-xs px-2 py-2px rounded-full tracking-wide uppercase"
|
|
[ngClass]="{'red-200': device?.device_status != 0,
|
|
'green-200': device?.device_status == 0}">
|
|
<span class="w-2 h-2 rounded-full mr-2"
|
|
[ngClass]="{'bg-red': device?.device_status != 0,
|
|
'bg-green': device?.device_status == 0}"></span>
|
|
<span class="pr-2px leading-relaxed whitespace-no-wrap">{{device?.device_status | deviceStatus}}</span>
|
|
</span>
|
|
</div>
|
|
<div class="text-secondary text-md">Status</div>
|
|
</div>
|
|
|
|
<div *ngIf="device?.host_id" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.host_id}}</div>
|
|
<div class="text-secondary text-md">Host ID</div>
|
|
</div>
|
|
|
|
<div *ngIf="device?.device_uuid" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.device_uuid}}</div>
|
|
<div class="text-secondary text-md">Device UUID</div>
|
|
</div>
|
|
<div *ngIf="device?.device_label" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.device_label}}</div>
|
|
<div class="text-secondary text-md">Device Label</div>
|
|
</div>
|
|
|
|
<div *ngIf="device?.device_type && device?.device_type != 'ata' && device?.device_type != 'scsi'" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.device_type | uppercase}}</div>
|
|
<div class="text-secondary text-md">Device Type</div>
|
|
</div>
|
|
<div *ngIf="device?.manufacturer" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.manufacturer}}</div>
|
|
<div class="text-secondary text-md">Model Family</div>
|
|
</div>
|
|
<div class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.model_name}}</div>
|
|
<div class="text-secondary text-md">Device Model</div>
|
|
</div>
|
|
<div class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.serial_number}}</div>
|
|
<div class="text-secondary text-md">Serial Number</div>
|
|
</div>
|
|
<div class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.wwn}}</div>
|
|
<div class="text-secondary text-md">LU WWN Device Id</div>
|
|
</div>
|
|
<div class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.firmware}}</div>
|
|
<div class="text-secondary text-md">Firmware Version</div>
|
|
</div>
|
|
<div class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.capacity | fileSize}}</div>
|
|
<div class="text-secondary text-md">Capacity</div>
|
|
</div>
|
|
<div *ngIf="device?.rotational_speed" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.rotational_speed}} RPM</div>
|
|
<div class="text-secondary text-md">Rotation Rate</div>
|
|
</div>
|
|
<div *ngIf="device?.device_protocol" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{device?.device_protocol}}</div>
|
|
<div class="text-secondary text-md">Protocol</div>
|
|
</div>
|
|
<div class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{smart_results[0]?.power_cycle_count}}</div>
|
|
<div class="text-secondary text-md">Power Cycle Count</div>
|
|
</div>
|
|
<div *ngIf="smart_results[0]?.power_on_hours" class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div matTooltip="{{humanizeDuration(smart_results[0]?.power_on_hours * 60 * 60 * 1000, { conjunction: ' and ', serialComma: false })}}">{{humanizeDuration(smart_results[0]?.power_on_hours *60 * 60 * 1000, { round: true, largest: 1, units: ['y', 'd', 'h'] })}}</div>
|
|
<div class="text-secondary text-md">Powered On</div>
|
|
</div>
|
|
<div class="my-2 col-span-2 lt-md:col-span-1">
|
|
<div>{{smart_results[0]?.temp | temperature:config.temperatureUnit:true}}</div>
|
|
<div class="text-secondary text-md">Temperature</div>
|
|
</div>
|
|
</div>
|
|
</treo-card>
|
|
</div>
|
|
<!-- S.M.A.R.T. Data table -->
|
|
<div class="flex flex-auto w-3/4 p-4 lt-md:w-full">
|
|
<div class="flex flex-col flex-auto w-full bg-card shadow-md rounded ">
|
|
<div class="p-6">
|
|
<div class="font-bold text-md text-secondary uppercase tracking-wider">S.M.A.R.T {{device?.device_protocol}} Attributes</div>
|
|
<div class="text-sm text-hint font-medium">{{this.smartAttributeDataSource.data.length}} visible, {{getHiddenAttributes()}} hidden</div>
|
|
</div>
|
|
<div class="overflow-auto">
|
|
<table class="w-full bg-transparent"
|
|
mat-table
|
|
matSort
|
|
multiTemplateDataRows
|
|
[dataSource]="smartAttributeDataSource"
|
|
[trackBy]="trackByFn"
|
|
#smartAttributeTable>
|
|
|
|
<!-- Status -->
|
|
<ng-container matColumnDef="status">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap">
|
|
Status
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
<span class="inline-flex items-center font-bold text-xs px-2 py-2px rounded-full tracking-wide uppercase"
|
|
[ngClass]="{'red-200': getAttributeStatusName(attribute.status) === 'failed',
|
|
'green-200': getAttributeStatusName(attribute.status) === 'passed',
|
|
'yellow-200': getAttributeStatusName(attribute.status) === 'warn'
|
|
}">
|
|
<span class="w-2 h-2 rounded-full mr-2"
|
|
[ngClass]="{'bg-red': getAttributeStatusName(attribute.status) === 'failed',
|
|
'bg-green': getAttributeStatusName(attribute.status) === 'passed',
|
|
'bg-yellow': getAttributeStatusName(attribute.status) === 'warn'}"></span>
|
|
<span class="pr-2px leading-relaxed whitespace-no-wrap" matTooltip="{{attribute.status_reason}}">{{getAttributeStatusName(attribute.status)}}</span>
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- ID -->
|
|
<ng-container matColumnDef="id">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap">
|
|
ID
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
<span class="pr-6 font-medium text-sm text-secondary whitespace-no-wrap">
|
|
{{attribute.attribute_id}} ({{toHex(attribute.attribute_id)}})
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Name -->
|
|
<ng-container matColumnDef="name">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap">
|
|
Name
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
<span class="pr-6 whitespace-no-wrap" matTooltip="click for more details.">
|
|
{{getAttributeName(attribute)}} <mat-icon *ngIf="getAttributeDescription(attribute)" class="icon-size-10" [svgIcon]="'info'"></mat-icon>
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Value -->
|
|
<ng-container matColumnDef="value">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap">
|
|
Value
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
<span class="pr-6 whitespace-no-wrap" matTooltip="{{getAttributeValueType(attribute)}}">
|
|
{{getAttributeValue(attribute)}}
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Worst -->
|
|
<ng-container matColumnDef="worst">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap">
|
|
Worst
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
<span class="pr-6 whitespace-no-wrap">
|
|
{{getAttributeWorst(attribute)}}
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Threshold -->
|
|
<ng-container matColumnDef="thresh">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap">
|
|
Threshold
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
<span class="pr-6 whitespace-no-wrap">
|
|
{{getAttributeThreshold(attribute)}}
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Ideal -->
|
|
<ng-container matColumnDef="ideal">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap">
|
|
Ideal
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
<span class="pr-6 font-medium whitespace-no-wrap">
|
|
{{getAttributeIdeal(attribute) }}
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- Observed Failure Rate -->
|
|
<ng-container matColumnDef="failure">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap" matTooltip="Failure rate is based on data provided by BackBlaze. The current attribute value is matched against the observed failure categories and an annual failure rate is determined.">
|
|
Failure Rate <mat-icon [svgIcon]="'info'"></mat-icon>
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
<span class="pr-6 font-medium whitespace-no-wrap">
|
|
{{attribute.failure_rate | percent}}
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<!-- History -->
|
|
<ng-container matColumnDef="history">
|
|
<th class="bg-cool-gray-50 dark:bg-cool-gray-700 border-t"
|
|
mat-header-cell
|
|
mat-sort-header
|
|
*matHeaderCellDef>
|
|
<span class="whitespace-no-wrap">
|
|
History
|
|
</span>
|
|
</th>
|
|
<td mat-cell
|
|
*matCellDef="let attribute">
|
|
|
|
<span class="font-medium whitespace-no-wrap">
|
|
<apx-chart
|
|
[series]="attribute.chartData"
|
|
[chart]="commonSparklineOptions.chart"
|
|
[tooltip]="commonSparklineOptions.tooltip"
|
|
[stroke]="commonSparklineOptions.stroke"
|
|
[annotations]="attribute.chartDataReferenceLine"
|
|
></apx-chart>
|
|
</span>
|
|
</td>
|
|
</ng-container>
|
|
|
|
|
|
<!-- Expanded Content Column - The detail row is made up of this one column that spans across all columns -->
|
|
<ng-container matColumnDef="expandedDetail">
|
|
<td mat-cell *matCellDef="let attribute" [attr.colspan]="smartAttributeTableColumns.length">
|
|
|
|
|
|
|
|
<div class="attribute-detail"
|
|
[@detailExpand]="attribute == expandedAttribute ? 'expanded' : 'collapsed'">
|
|
|
|
<div class="flex flex-auto w-1/3 min-w-80 py-4">
|
|
<div class="flex flex-col flex-auto justify-end text-md pb-3">
|
|
{{getAttributeDescription(attribute)}}
|
|
</div>
|
|
</div>
|
|
<div class="flex flex-auto w-2/3 min-w-80">
|
|
<div class="flex flex-col flex-auto justify-end text-md px-6 pb-3">
|
|
<div class="flex items-center justify-between py-3 border-b last:border-b-0 ng-star-inserted">
|
|
<div class="flex items-center w-1/4">Type</div>
|
|
<div class="flex items-center w-1/4">Value</div>
|
|
<div class="flex items-center w-1/4">Worst/Thresh</div>
|
|
<div class="flex items-center w-1/4">Failure %</div>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between py-3 border-b last:border-b-0 ng-star-inserted">
|
|
<div class="flex items-center w-1/4">
|
|
<div class="flex-shrink-0 w-2 h-2 mr-3 rounded-full"
|
|
[ngClass]="{'bg-red': getAttributeScrutinyStatusName(attribute.status) === 'failed',
|
|
'bg-green': getAttributeScrutinyStatusName(attribute.status) === 'passed',
|
|
'bg-yellow': getAttributeScrutinyStatusName(attribute.status) === 'warn'}"></div>
|
|
<div class="truncate">Scrutiny</div>
|
|
</div>
|
|
<div class="w-1/4 items-center font-medium">{{getAttributeValue(attribute)}}</div>
|
|
<div class="w-1/4 items-center text-secondary">--</div>
|
|
<div class="w-1/4 items-center text-secondary">{{(attribute.failure_rate | percent) || '--'}}</div>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between py-3 border-b last:border-b-0 ng-star-inserted">
|
|
<div class="flex items-center w-1/4">
|
|
<div class="flex-shrink-0 w-2 h-2 mr-3 rounded-full"
|
|
[ngClass]="{'bg-red': getAttributeSmartStatusName(attribute.status) === 'failed',
|
|
'bg-green': getAttributeSmartStatusName(attribute.status) === 'passed'}"
|
|
></div>
|
|
<div class="truncate">Normalized</div>
|
|
</div>
|
|
<div class="w-1/4 items-center font-medium">{{attribute.value}}</div>
|
|
<div class="w-1/4 items-center text-secondary">{{getAttributeWorst(attribute) || '--' }}/{{getAttributeThreshold(attribute)}}</div>
|
|
<div class="w-1/4 items-center text-secondary">--</div>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-between py-3 border-b last:border-b-0 ng-star-inserted">
|
|
<div class="flex items-center w-1/4">
|
|
<div class="flex-shrink-0 w-2 h-2 mr-3 rounded-full"></div>
|
|
<div class="truncate">Raw</div>
|
|
</div>
|
|
<div class="w-1/4 items-center font-medium">{{attribute.raw_value}}</div>
|
|
<div class="w-1/4 items-center text-secondary">--</div>
|
|
<div class="w-1/4 items-center text-secondary">--</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
</ng-container>
|
|
|
|
|
|
<!-- Footer -->
|
|
<ng-container matColumnDef="recentOrdersTableFooter">
|
|
<td class="px-3 border-none"
|
|
mat-footer-cell
|
|
*matFooterCellDef
|
|
colspan="6">
|
|
<button mat-button
|
|
(click)="toggleOnlyCritical()"
|
|
[color]="'primary'">
|
|
<span *ngIf="onlyCritical">Show all attributes</span>
|
|
<span *ngIf="!onlyCritical">Show critical attributes</span>
|
|
</button>
|
|
</td>
|
|
</ng-container>
|
|
|
|
<tr mat-header-row
|
|
*matHeaderRowDef="smartAttributeTableColumns"></tr>
|
|
<tr class="attribute-row h-16"
|
|
mat-row
|
|
[ngClass]="{'yellow-50': getAttributeCritical(row)}"
|
|
[class.attribute-expanded-row]="expandedAttribute === row"
|
|
(click)="expandedAttribute = expandedAttribute === row ? null : row"
|
|
*matRowDef="let row; columns: smartAttributeTableColumns;"></tr>
|
|
<tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="attribute-detail-row"></tr>
|
|
<tr class="h-16"
|
|
mat-footer-row
|
|
*matFooterRowDef="['recentOrdersTableFooter']"></tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|