-
Notifications
You must be signed in to change notification settings - Fork 35
/
__init__.py
785 lines (693 loc) · 41.8 KB
/
__init__.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
from enum import Enum
import re
import humanfriendly
from typing import Optional, Iterator, Union, List
from ..common import CommonIface
class NvmeStatus(Enum):
# Codes have been extraced from https://github.com/RobinTMiller/dt/blob/master/nvme_lib.h
# Copyright (c) 2011-2014, Intel Corporation.
# Generic Command
NVME_SC_SUCCESS = 0X0
NVME_SC_INVALID_OPCODE = 0X1
NVME_SC_INVALID_FIELD = 0X2
NVME_SC_CMDID_CONFLICT = 0X3
NVME_SC_DATA_XFER_ERROR = 0X4
NVME_SC_POWER_LOSS = 0X5
NVME_SC_INTERNAL = 0X6
NVME_SC_ABORT_REQ = 0X7
NVME_SC_ABORT_QUEUE = 0X8
NVME_SC_FUSED_FAIL = 0X9
NVME_SC_FUSED_MISSING = 0XA
NVME_SC_INVALID_NS = 0XB
NVME_SC_CMD_SEQ_ERROR = 0XC
NVME_SC_SGL_INVALID_LAST = 0XD
NVME_SC_SGL_INVALID_COUNT = 0XE
NVME_SC_SGL_INVALID_DATA = 0XF
NVME_SC_SGL_INVALID_METADATA = 0X10
NVME_SC_SGL_INVALID_TYPE = 0X11
NVME_SC_CMB_INVALID_USE = 0X12
NVME_SC_PRP_INVALID_OFFSET = 0X13
NVME_SC_ATOMIC_WRITE_UNIT_EXCEEDED = 0X14
NVME_SC_OPERATION_DENIED = 0X15
NVME_SC_SGL_INVALID_OFFSET = 0X16
NVME_SC_INCONSISTENT_HOST_ID = 0X18
NVME_SC_KEEP_ALIVE_EXPIRED = 0X19
NVME_SC_KEEP_ALIVE_INVALID = 0X1A
NVME_SC_PREEMPT_ABORT = 0X1B
NVME_SC_SANITIZE_FAILED = 0X1C
NVME_SC_SANITIZE_IN_PROGRESS = 0X1D
NVME_SC_NS_WRITE_PROTECTED = 0X20
NVME_SC_CMD_INTERRUPTED = 0X21
NVME_SC_TRANSIENT_TRANSPORT = 0X22
NVME_SC_LBA_RANGE = 0X80
NVME_SC_CAP_EXCEEDED = 0X81
NVME_SC_NS_NOT_READY = 0X82
NVME_SC_RESERVATION_CONFLICT = 0X83
NVME_SC_FORMAT_IN_PROGRESS = 0X84
# Command Specific Status:
NVME_SC_CQ_INVALID = 0X100
NVME_SC_QID_INVALID = 0X101
NVME_SC_QUEUE_SIZE = 0X102
NVME_SC_ABORT_LIMIT = 0X103
NVME_SC_ABORT_MISSING = 0X104
NVME_SC_ASYNC_LIMIT = 0X105
NVME_SC_FIRMWARE_SLOT = 0X106
NVME_SC_FIRMWARE_IMAGE = 0X107
NVME_SC_INVALID_VECTOR = 0X108
NVME_SC_INVALID_LOG_PAGE = 0X109
NVME_SC_INVALID_FORMAT = 0X10A
NVME_SC_FW_NEEDS_CONV_RESET = 0X10B
NVME_SC_INVALID_QUEUE = 0X10C
NVME_SC_FEATURE_NOT_SAVEABLE = 0X10D
NVME_SC_FEATURE_NOT_CHANGEABLE = 0X10E
NVME_SC_FEATURE_NOT_PER_NS = 0X10F
NVME_SC_FW_NEEDS_SUBSYS_RESET = 0X110
NVME_SC_FW_NEEDS_RESET = 0X111
NVME_SC_FW_NEEDS_MAX_TIME = 0X112
NVME_SC_FW_ACTIVATE_PROHIBITED = 0X113
NVME_SC_OVERLAPPING_RANGE = 0X114
NVME_SC_NS_INSUFFICIENT_CAP = 0X115
NVME_SC_NS_ID_UNAVAILABLE = 0X116
NVME_SC_NS_ALREADY_ATTACHED = 0X118
NVME_SC_NS_IS_PRIVATE = 0X119
NVME_SC_NS_NOT_ATTACHED = 0X11A
NVME_SC_THIN_PROV_NOT_SUPP = 0X11B
NVME_SC_CTRL_LIST_INVALID = 0X11C
NVME_SC_DEVICE_SELF_TEST_IN_PROGRESS = 0X11D
NVME_SC_BP_WRITE_PROHIBITED = 0X11E
NVME_SC_INVALID_CTRL_ID = 0X11F
NVME_SC_INVALID_SECONDARY_CTRL_STATE = 0X120
NVME_SC_INVALID_NUM_CTRL_RESOURCE = 0X121
NVME_SC_INVALID_RESOURCE_ID = 0X122
NVME_SC_PMR_SAN_PROHIBITED = 0X123
NVME_SC_ANA_INVALID_GROUP_ID = 0X124
NVME_SC_ANA_ATTACH_FAIL = 0X125
# Command Set Specific - Namespace Types commands:
NVME_SC_IOCS_NOT_SUPPORTED = 0X129
NVME_SC_IOCS_NOT_ENABLED = 0X12A
NVME_SC_IOCS_COMBINATION_REJECTED = 0X12B
NVME_SC_INVALID_IOCS = 0X12C
# I/O Command Set Specific - NVM commands:
NVME_SC_BAD_ATTRIBUTES = 0X180
NVME_SC_INVALID_PI = 0X181
NVME_SC_READ_ONLY = 0X182
NVME_SC_CMD_SIZE_LIMIT_EXCEEDED = 0X183
# I/O Command Set Specific - Fabrics commands:
NVME_SC_CONNECT_FORMAT = 0X180
NVME_SC_CONNECT_CTRL_BUSY = 0X181
NVME_SC_CONNECT_INVALID_PARAM = 0X182
NVME_SC_CONNECT_RESTART_DISC = 0X183
NVME_SC_CONNECT_INVALID_HOST = 0X184
NVME_SC_DISCOVERY_RESTART = 0X190
NVME_SC_AUTH_REQUIRED = 0X191
# I/O Command Set Specific - Zoned Namespace commands:
NVME_SC_ZONE_BOUNDARY_ERROR = 0X1B8
NVME_SC_ZONE_IS_FULL = 0X1B9
NVME_SC_ZONE_IS_READ_ONLY = 0X1BA
NVME_SC_ZONE_IS_OFFLINE = 0X1BB
NVME_SC_ZONE_INVALID_WRITE = 0X1BC
NVME_SC_TOO_MANY_ACTIVE_ZONES = 0X1BD
NVME_SC_TOO_MANY_OPEN_ZONES = 0X1BE
NVME_SC_ZONE_INVALID_STATE_TRANSITION = 0X1BF
# Media and Data Integrity Errors:
NVME_SC_WRITE_FAULT = 0X280
NVME_SC_READ_ERROR = 0X281
NVME_SC_GUARD_CHECK = 0X282
NVME_SC_APPTAG_CHECK = 0X283
NVME_SC_REFTAG_CHECK = 0X284
NVME_SC_COMPARE_FAILED = 0X285
NVME_SC_ACCESS_DENIED = 0X286
NVME_SC_UNWRITTEN_BLOCK = 0X287
# Path-related Errors:
NVME_SC_INTERNAL_PATH_ERROR = 0X300
NVME_SC_ANA_PERSISTENT_LOSS = 0X301
NVME_SC_ANA_INACCESSIBLE = 0X302
NVME_SC_ANA_TRANSITION = 0X303
# Controller Detected Path errors
NVME_SC_CTRL_PATHING_ERROR = 0X360
# Host Detected Path Errors
NVME_SC_HOST_PATHING_ERROR = 0X370
NVME_SC_HOST_CMD_ABORT = 0X371
NVME_SC_CRD = 0X1800
NVME_SC_DNR = 0X4000
class NvmeError(object):
"""This class stores the nvme detected errors
The expected error table looks like:
Num ErrCount SQId CmdId Status PELoc LBA NSID VS
0 1356 0 0x0012 0xc005 0x028 - 0 -
Attributes:
id : The error number or id
count : The number of errors detected
sqid : The submission queue id
cmdid : The command id
status : The status of the error
peloc : The physical error location
lba : The logical block address
nsid : The namespace id
vs : The vendor specific
cs : The command specific
"""
def __init__(self, num: int, errCount: int, sqId: int, cmdId: int, status: int, peLoc: int, lba: Optional[int] = None, nsid: Optional[int] = None, vs: Optional[int] = None):
self.num: int = num
self.errCount: int = errCount
self.sqId: int = sqId
self.cmdId: int = cmdId
self.status: int = status
self.peLoc: int = peLoc
self.lba: Optional[int] = lba
self.nsid: Optional[int] = nsid
self.vs: Optional[int] = vs
self.cs: Optional[int] = None
@property
def status_str(self) -> str:
# try to convert the status to a string
# Strings have been extraced from https://github.com/RobinTMiller/dt/blob/master/nvme_lib.c
# Copyright (c) 2011-2014, Intel Corporation.
status = self.status & 0x7ff
if status == NvmeStatus.NVME_SC_SUCCESS:
status_str = "SUCCESS: The command completed successfully"
elif status == NvmeStatus.NVME_SC_INVALID_OPCODE:
status_str = "INVALID_OPCODE: The associated command opcode field is not valid"
elif status == NvmeStatus.NVME_SC_INVALID_FIELD:
status_str = "INVALID_FIELD: A reserved coded value or an unsupported value in a defined field"
elif status == NvmeStatus.NVME_SC_CMDID_CONFLICT:
status_str = "CMDID_CONFLICT: The command identifier is already in use"
elif status == NvmeStatus.NVME_SC_DATA_XFER_ERROR:
status_str = "DATA_XFER_ERROR: Error while trying to transfer the data or metadata"
elif status == NvmeStatus.NVME_SC_POWER_LOSS:
status_str = "POWER_LOSS: Command aborted due to power loss notification"
elif status == NvmeStatus.NVME_SC_INTERNAL:
status_str = "INTERNAL: The command was not completed successfully due to an internal error"
elif status == NvmeStatus.NVME_SC_ABORT_REQ:
status_str = "ABORT_REQ: The command was aborted due to a Command Abort request"
elif status == NvmeStatus.NVME_SC_ABORT_QUEUE:
status_str = "ABORT_QUEUE: The command was aborted due to a Delete I/O Submission Queue request"
elif status == NvmeStatus.NVME_SC_FUSED_FAIL:
status_str = "FUSED_FAIL: The command was aborted due to the other command in a fused operation failing"
elif status == NvmeStatus.NVME_SC_FUSED_MISSING:
status_str = "FUSED_MISSING: The command was aborted due to a Missing Fused Command"
elif status == NvmeStatus.NVME_SC_INVALID_NS:
status_str = "INVALID_NS: The namespace or the format of that namespace is invalid"
elif status == NvmeStatus.NVME_SC_CMD_SEQ_ERROR:
status_str = "CMD_SEQ_ERROR: The command was aborted due to a protocol violation in a multicommand sequence"
elif status == NvmeStatus.NVME_SC_SGL_INVALID_LAST:
status_str = "SGL_INVALID_LAST: The command includes an invalid SGL Last Segment or SGL Segment descriptor."
elif status == NvmeStatus.NVME_SC_SGL_INVALID_COUNT:
status_str = "SGL_INVALID_COUNT: There is an SGL Last Segment descriptor or an SGL Segment descriptor in a location other than the last descriptor of a segment based on the length indicated."
elif status == NvmeStatus.NVME_SC_SGL_INVALID_DATA:
status_str = "SGL_INVALID_DATA: This may occur if the length of a Data SGL is too short."
elif status == NvmeStatus.NVME_SC_SGL_INVALID_METADATA:
status_str = "SGL_INVALID_METADATA: This may occur if the length of a Metadata SGL is too short"
elif status == NvmeStatus.NVME_SC_SGL_INVALID_TYPE:
status_str = "SGL_INVALID_TYPE: The type of an SGL Descriptor is a type that is not supported by the controller."
elif status == NvmeStatus.NVME_SC_CMB_INVALID_USE:
status_str = "CMB_INVALID_USE: The attempted use of the Controller Memory Buffer is not supported by the controller."
elif status == NvmeStatus.NVME_SC_PRP_INVALID_OFFSET:
status_str = "PRP_INVALID_OFFSET: The Offset field for a PRP entry is invalid."
elif status == NvmeStatus.NVME_SC_ATOMIC_WRITE_UNIT_EXCEEDED:
status_str = "ATOMIC_WRITE_UNIT_EXCEEDED: The length specified exceeds the atomic write unit size."
elif status == NvmeStatus.NVME_SC_OPERATION_DENIED:
status_str = "OPERATION_DENIED: The command was denied due to lack of access rights."
elif status == NvmeStatus.NVME_SC_SGL_INVALID_OFFSET:
status_str = "SGL_INVALID_OFFSET: The offset specified in a descriptor is invalid."
elif status == NvmeStatus.NVME_SC_INCONSISTENT_HOST_ID:
status_str = "INCONSISTENT_HOST_ID: The NVM subsystem detected the simultaneous use of 64-bit and 128-bit Host Identifier values on different controllers."
elif status == NvmeStatus.NVME_SC_KEEP_ALIVE_EXPIRED:
status_str = "KEEP_ALIVE_EXPIRED: The Keep Alive Timer expired."
elif status == NvmeStatus.NVME_SC_KEEP_ALIVE_INVALID:
status_str = "KEEP_ALIVE_INVALID: The Keep Alive Timeout value specified is invalid."
elif status == NvmeStatus.NVME_SC_PREEMPT_ABORT:
status_str = "PREEMPT_ABORT: The command was aborted due to a Reservation Acquire command with the Reservation Acquire Action (RACQA) set to 010b (Preempt and Abort)."
elif status == NvmeStatus.NVME_SC_SANITIZE_FAILED:
status_str = "SANITIZE_FAILED: The most recent sanitize operation failed and no recovery actions has been successfully completed"
elif status == NvmeStatus.NVME_SC_SANITIZE_IN_PROGRESS:
status_str = "SANITIZE_IN_PROGRESS: The requested function is prohibited while a sanitize operation is in progress"
elif status == NvmeStatus.NVME_SC_IOCS_NOT_SUPPORTED:
status_str = "IOCS_NOT_SUPPORTED: The I/O command set is not supported"
elif status == NvmeStatus.NVME_SC_IOCS_NOT_ENABLED:
status_str = "IOCS_NOT_ENABLED: The I/O command set is not enabled"
elif status == NvmeStatus.NVME_SC_IOCS_COMBINATION_REJECTED:
status_str = "IOCS_COMBINATION_REJECTED: The I/O command set combination is rejected"
elif status == NvmeStatus.NVME_SC_INVALID_IOCS:
status_str = "INVALID_IOCS: the I/O command set is invalid"
elif status == NvmeStatus.NVME_SC_LBA_RANGE:
status_str = "LBA_RANGE: The command references a LBA that exceeds the size of the namespace"
elif status == NvmeStatus.NVME_SC_NS_WRITE_PROTECTED:
status_str = "NS_WRITE_PROTECTED: The command is prohibited while the namespace is write protected by the host."
elif status == NvmeStatus.NVME_SC_TRANSIENT_TRANSPORT:
status_str = "TRANSIENT_TRANSPORT: A transient transport error was detected."
elif status == NvmeStatus.NVME_SC_CAP_EXCEEDED:
status_str = "CAP_EXCEEDED: The execution of the command has caused the capacity of the namespace to be exceeded"
elif status == NvmeStatus.NVME_SC_NS_NOT_READY:
status_str = "NS_NOT_READY: The namespace is not ready to be accessed as a result of a condition other than a condition that is reported as an Asymmetric Namespace Access condition"
elif status == NvmeStatus.NVME_SC_RESERVATION_CONFLICT:
status_str = "RESERVATION_CONFLICT: The command was aborted due to a conflict with a reservation held on the accessed namespace"
elif status == NvmeStatus.NVME_SC_FORMAT_IN_PROGRESS:
status_str = "FORMAT_IN_PROGRESS: A Format NVM command is in progress on the namespace."
elif status == NvmeStatus.NVME_SC_ZONE_BOUNDARY_ERROR:
status_str = "ZONE_BOUNDARY_ERROR: Invalid Zone Boundary crossing"
elif status == NvmeStatus.NVME_SC_ZONE_IS_FULL:
status_str = "ZONE_IS_FULL: The accessed zone is in ZSF:Full state"
elif status == NvmeStatus.NVME_SC_ZONE_IS_READ_ONLY:
status_str = "ZONE_IS_READ_ONLY: The accessed zone is in ZSRO:Read Only state"
elif status == NvmeStatus.NVME_SC_ZONE_IS_OFFLINE:
status_str = "ZONE_IS_OFFLINE: The access zone is in ZSO:Offline state"
elif status == NvmeStatus.NVME_SC_ZONE_INVALID_WRITE:
status_str = "ZONE_INVALID_WRITE: The write to zone was not at the write pointer offset"
elif status == NvmeStatus.NVME_SC_TOO_MANY_ACTIVE_ZONES:
status_str = "TOO_MANY_ACTIVE_ZONES: The controller does not allow additional active zones"
elif status == NvmeStatus.NVME_SC_TOO_MANY_OPEN_ZONES:
status_str = "TOO_MANY_OPEN_ZONES: The controller does not allow additional open zones"
elif status == NvmeStatus.NVME_SC_ZONE_INVALID_STATE_TRANSITION:
status_str = "INVALID_ZONE_STATE_TRANSITION: The zone state change was invalid"
elif status == NvmeStatus.NVME_SC_CQ_INVALID:
status_str = "CQ_INVALID: The Completion Queue identifier specified in the command does not exist"
elif status == NvmeStatus.NVME_SC_QID_INVALID:
status_str = "QID_INVALID: The creation of the I/O Completion Queue failed due to an invalid queue identifier specified as part of the command. An invalid queue identifier is one that is currently in use or one that is outside the range supported by the controller"
elif status == NvmeStatus.NVME_SC_QUEUE_SIZE:
status_str = "QUEUE_SIZE: The host attempted to create an I/O Completion Queue with an invalid number of entries"
elif status == NvmeStatus.NVME_SC_ABORT_LIMIT:
status_str = "ABORT_LIMIT: The number of concurrently outstanding Abort commands has exceeded the limit indicated in the Identify Controller data structure"
elif status == NvmeStatus.NVME_SC_ABORT_MISSING:
status_str = "ABORT_MISSING: The abort command is missing"
elif status == NvmeStatus.NVME_SC_ASYNC_LIMIT:
status_str = "ASYNC_LIMIT: The number of concurrently outstanding Asynchronous Event Request commands has been exceeded"
elif status == NvmeStatus.NVME_SC_FIRMWARE_SLOT:
status_str = "FIRMWARE_SLOT: The firmware slot indicated is invalid or read only. This error is indicated if the firmware slot exceeds the number supported"
elif status == NvmeStatus.NVME_SC_FIRMWARE_IMAGE:
status_str = "FIRMWARE_IMAGE: The firmware image specified for activation is invalid and not loaded by the controller"
elif status == NvmeStatus.NVME_SC_INVALID_VECTOR:
status_str = "INVALID_VECTOR: The creation of the I/O Completion Queue failed due to an invalid interrupt vector specified as part of the command"
elif status == NvmeStatus.NVME_SC_INVALID_LOG_PAGE:
status_str = "INVALID_LOG_PAGE: The log page indicated is invalid. This error condition is also returned if a reserved log page is requested"
elif status == NvmeStatus.NVME_SC_INVALID_FORMAT:
status_str = "INVALID_FORMAT: The LBA Format specified is not supported. This may be due to various conditions"
elif status == NvmeStatus.NVME_SC_FW_NEEDS_CONV_RESET:
status_str = "FW_NEEDS_CONVENTIONAL_RESET: The firmware commit was successful, however, activation of the firmware image requires a conventional reset"
elif status == NvmeStatus.NVME_SC_INVALID_QUEUE:
status_str = "INVALID_QUEUE: This error indicates that it is invalid to delete the I/O Completion Queue specified. The typical reason for this error condition is that there is an associated I/O Submission Queue that has not been deleted."
elif status == NvmeStatus.NVME_SC_FEATURE_NOT_SAVEABLE:
status_str = "FEATURE_NOT_SAVEABLE: The Feature Identifier specified does not support a saveable value"
elif status == NvmeStatus.NVME_SC_FEATURE_NOT_CHANGEABLE:
status_str = "FEATURE_NOT_CHANGEABLE: The Feature Identifier is not able to be changed"
elif status == NvmeStatus.NVME_SC_FEATURE_NOT_PER_NS:
status_str = "FEATURE_NOT_PER_NS: The Feature Identifier specified is not namespace specific. The Feature Identifier settings apply across all namespaces"
elif status == NvmeStatus.NVME_SC_FW_NEEDS_SUBSYS_RESET:
status_str = "FW_NEEDS_SUBSYSTEM_RESET: The firmware commit was successful, however, activation of the firmware image requires an NVM Subsystem"
elif status == NvmeStatus.NVME_SC_FW_NEEDS_RESET:
status_str = "FW_NEEDS_RESET: The firmware commit was successful; however, the image specified does not support being activated without a reset"
elif status == NvmeStatus.NVME_SC_FW_NEEDS_MAX_TIME:
status_str = "FW_NEEDS_MAX_TIME_VIOLATION: The image specified if activated immediately would exceed the Maximum Time for Firmware Activation (MTFA) value reported in Identify Controller. To activate the firmware, the Firmware Commit command needs to be re-issued and the image activated using a reset"
elif status == NvmeStatus.NVME_SC_FW_ACTIVATE_PROHIBITED:
status_str = "FW_ACTIVATION_PROHIBITED: The image specified is being prohibited from activation by the controller for vendor specific reasons"
elif status == NvmeStatus.NVME_SC_OVERLAPPING_RANGE:
status_str = "OVERLAPPING_RANGE: This error is indicated if the firmware image has overlapping ranges"
elif status == NvmeStatus.NVME_SC_NS_INSUFFICIENT_CAP:
status_str = "NS_INSUFFICIENT_CAPACITY: Creating the namespace requires more free space than is currently available. The Command Specific Information field of the Error Information Log specifies the total amount of NVM capacity required to create the namespace in bytes"
elif status == NvmeStatus.NVME_SC_NS_ID_UNAVAILABLE:
status_str = "NS_ID_UNAVAILABLE: The number of namespaces supported has been exceeded"
elif status == NvmeStatus.NVME_SC_NS_ALREADY_ATTACHED:
status_str = "NS_ALREADY_ATTACHED: The controller is already attached to the namespace specified"
elif status == NvmeStatus.NVME_SC_NS_IS_PRIVATE:
status_str = "NS_IS_PRIVATE: The namespace is private and is already attached to one controller"
elif status == NvmeStatus.NVME_SC_NS_NOT_ATTACHED:
status_str = "NS_NOT_ATTACHED: The request to detach the controller could not be completed because the controller is not attached to the namespace"
elif status == NvmeStatus.NVME_SC_THIN_PROV_NOT_SUPP:
status_str = "THIN_PROVISIONING_NOT_SUPPORTED: Thin provisioning is not supported by the controller"
elif status == NvmeStatus.NVME_SC_CTRL_LIST_INVALID:
status_str = "CONTROLLER_LIST_INVALID: The controller list provided is invalid"
elif status == NvmeStatus.NVME_SC_DEVICE_SELF_TEST_IN_PROGRESS:
status_str = "DEVICE_SELF_TEST_IN_PROGRESS: The controller or NVM subsystem already has a device self-test operation in process."
elif status == NvmeStatus.NVME_SC_BP_WRITE_PROHIBITED:
status_str = "BOOT PARTITION WRITE PROHIBITED: The command is trying to modify a Boot Partition while it is locked"
elif status == NvmeStatus.NVME_SC_INVALID_CTRL_ID:
status_str = "INVALID_CTRL_ID: An invalid Controller Identifier was specified."
elif status == NvmeStatus.NVME_SC_INVALID_SECONDARY_CTRL_STATE:
status_str = "INVALID_SECONDARY_CTRL_STATE: The action requested for the secondary controller is invalid based on the current state of the secondary controller and its primary controller."
elif status == NvmeStatus.NVME_SC_INVALID_NUM_CTRL_RESOURCE:
status_str = "INVALID_NUM_CTRL_RESOURCE: The specified number of Flexible Resources is invalid"
elif status == NvmeStatus.NVME_SC_INVALID_RESOURCE_ID:
status_str = "INVALID_RESOURCE_ID: At least one of the specified resource identifiers was invalid"
elif status == NvmeStatus.NVME_SC_ANA_INVALID_GROUP_ID:
status_str = "ANA_INVALID_GROUP_ID: The specified ANA Group Identifier (ANAGRPID) is not supported in the submitted command."
elif status == NvmeStatus.NVME_SC_ANA_ATTACH_FAIL:
status_str = "ANA_ATTACH_FAIL: The controller is not attached to the namespace as a result of an ANA condition"
elif status == NvmeStatus.NVME_SC_BAD_ATTRIBUTES:
status_str = "BAD_ATTRIBUTES: Bad attributes were given"
elif status == NvmeStatus.NVME_SC_INVALID_PI:
status_str = "INVALID_PROTECION_INFO: The Protection Information Field settings specified in the command are invalid"
elif status == NvmeStatus.NVME_SC_READ_ONLY:
status_str = "WRITE_ATTEMPT_READ_ONLY_RANGE: The LBA range specified contains read-only blocks"
elif status == NvmeStatus.NVME_SC_CMD_SIZE_LIMIT_EXCEEDED:
status_str = "CMD_SIZE_LIMIT_EXCEEDED: Command size limit exceeded"
elif status == NvmeStatus.NVME_SC_WRITE_FAULT:
status_str = "WRITE_FAULT: The write data could not be committed to the media"
elif status == NvmeStatus.NVME_SC_READ_ERROR:
status_str = "READ_ERROR: The read data could not be recovered from the media"
elif status == NvmeStatus.NVME_SC_GUARD_CHECK:
status_str = "GUARD_CHECK: The command was aborted due to an end-to-end guard check failure"
elif status == NvmeStatus.NVME_SC_APPTAG_CHECK:
status_str = "APPTAG_CHECK: The command was aborted due to an end-to-end application tag check failure"
elif status == NvmeStatus.NVME_SC_REFTAG_CHECK:
status_str = "REFTAG_CHECK: The command was aborted due to an end-to-end reference tag check failure"
elif status == NvmeStatus.NVME_SC_COMPARE_FAILED:
status_str = "COMPARE_FAILED: The command failed due to a miscompare during a Compare command"
elif status == NvmeStatus.NVME_SC_ACCESS_DENIED:
status_str = "ACCESS_DENIED: Access to the namespace and/or LBA range is denied due to lack of access rights"
elif status == NvmeStatus.NVME_SC_UNWRITTEN_BLOCK:
status_str = "UNWRITTEN_BLOCK: The command failed due to an attempt to read from an LBA range containing a deallocated or unwritten logical block"
elif status == NvmeStatus.NVME_SC_INTERNAL_PATH_ERROR:
status_str = "INTERNAL_PATH_ERROT: The command was not completed as the result of a controller internal error"
elif status == NvmeStatus.NVME_SC_ANA_PERSISTENT_LOSS:
status_str = "ASYMMETRIC_NAMESPACE_ACCESS_PERSISTENT_LOSS: The requested function (e.g., command) is not able to be performed as a result of the relationship between the controller and the namespace being in the ANA Persistent Loss state"
elif status == NvmeStatus.NVME_SC_ANA_INACCESSIBLE:
status_str = "ASYMMETRIC_NAMESPACE_ACCESS_INACCESSIBLE: The requested function (e.g., command) is not able to be performed as a result of the relationship between the controller and the namespace being in the ANA Inaccessible state"
elif status == NvmeStatus.NVME_SC_ANA_TRANSITION:
status_str = "ASYMMETRIC_NAMESPACE_ACCESS_TRANSITION: The requested function (e.g., command) is not able to be performed as a result of the relationship between the controller and the namespace transitioning between Asymmetric Namespace Access states"
elif status == NvmeStatus.NVME_SC_CTRL_PATHING_ERROR:
status_str = "CONTROLLER_PATHING_ERROR: A pathing error was detected by the controller"
elif status == NvmeStatus.NVME_SC_HOST_PATHING_ERROR:
status_str = "HOST_PATHING_ERROR: A pathing error was detected by the host"
elif status == NvmeStatus.NVME_SC_HOST_CMD_ABORT:
status_str = "HOST_COMMAND_ABORT: The command was aborted as a result of host action"
elif status == NvmeStatus.NVME_SC_CMD_INTERRUPTED:
status_str = "CMD_INTERRUPTED: Command processing was interrupted and the controller is unable to successfully complete the command. The host should retry the command."
elif status == NvmeStatus.NVME_SC_PMR_SAN_PROHIBITED:
status_str = "Sanitize Prohibited While Persistent Memory Region is Enabled: A sanitize operation is prohibited while the Persistent Memory Region is enabled."
else:
status_str = "Unknown"
# return str(self.status)+f': ({status_str})'
return status_str
def __str__(self):
return self.__repr__()
def __repr__(self):
return f'{self.status}: ({self.status_str})'
def __getstate__(self, all_info=True):
"""
Allows us to send a pySMART diagnostics object over a serializable
medium which uses json (or the likes of json) payloads
"""
return vars(self)
def __setstate__(self, state):
self.__dict__.update(state)
class NvmeSelfTest(object):
"""This class represents a test entry of a NVMe device
Attributes:
num (int): The test number
description (str): The test description
status (str): The test status
powerOnHours (int): The power on hours
failingLBA (Optional[int]): The failing LBA
nsid (Optional[int]): The namespace ID
sct (Optional[str]): The SCT
code (Optional[str]): The code
progress (int): The progress of the test. Defaults to 100%
"""
def __init__(self, num: int, description: str, status: str, powerOnHours: int, failingLBA: Optional[int] = None, nsid: Optional[int] = None, sct: Optional[str] = None, code: Optional[str] = None, progress: int = 100):
self.num: int = num
self.description: str = description
self.status: str = status
self.powerOnHours: int = powerOnHours
self.failingLBA: Optional[int] = failingLBA
self.nsid: Optional[int] = nsid
self.sct: Optional[str] = sct
self.code: Optional[str] = code
self.progress: int = progress
def __str__(self):
return self.__repr__()
def __repr__(self):
# Example smartctl output
# Self-test Log (NVMe Log 0x06)
# Self-test status: Extended self-test in progress (28% completed)
# Num Test_Description Status Power_on_Hours Failing_LBA NSID SCT Code
# 0 Extended Completed without error 3441 - - - -
return ("{0:>2} {1:18}{2:29}{3:16}{4:13}{5:5}{6:4}{7:4}".format(
self.num,
self.description,
self.status,
self.powerOnHours,
self.failingLBA,
self.nsid,
self.sct,
self.code
))
def __getstate__(self, all_info=True):
"""
Allows us to send a pySMART diagnostics object over a serializable
medium which uses json (or the likes of json) payloads
"""
return vars(self)
def __setstate__(self, state):
self.__dict__.update(state)
class NvmeAttributes(CommonIface):
"""This class represents the attributes of a NVMe device
Attributes:
criticalWarning : Number of critical warnings
temperature : Temperature in Celsius
availableSpare : Available spare in percentage
availableSpareThreshold : Available spare threshold in percentage
percentageUsed : Data units used in percentage
dataUnitsRead : Data units (sectors) read
bytesRead : Bytes read
dataUnitsWritten : Data units (sectors) written
bytesWritten : Bytes written
hostReadCommands : Host read commands
hostWriteCommands : Host write commands
controllerBusyTime : Controller busy time in minutes
powerCycles : Power on cycles
powerOnHours : Power on hours
unsafeShutdowns : Unsafe shutdowns
integrityErrors : Integrity errors
errorEntries : Error log entries
warningTemperatureTime : Time in minutes at warning temperature
criticalTemperatureTime : Time in minutes at critical temperature
errors : List of errors
"""
def __init__(self, data: Optional[Iterator[str]] = None):
"""Initializes the attributes
Args:
data (Iterator[str], optional): Iterator of the lines of the output of the command nvme smart-log. Defaults to None.
"""
self.critialWarning: Optional[int] = None
self._temperature: Optional[int] = None
self.availableSpare: Optional[int] = None
self.availableSpareThreshold: Optional[int] = None
self.percentageUsed: Optional[int] = None
self.dataUnitsRead: Optional[int] = None
self.bytesRead: Optional[int] = None
self.dataUnitsWritten: Optional[int] = None
self.bytesWritten: Optional[int] = None
self.hostReadCommands: Optional[int] = None
self.hostWriteCommands: Optional[int] = None
self.controllerBusyTime: Optional[int] = None
self.powerCycles: Optional[int] = None
self.powerOnHours: Optional[int] = None
self.unsafeShutdowns: Optional[int] = None
self.integrityErrors: Optional[int] = None
self.errorEntries: Optional[int] = None
self.warningTemperatureTime: Optional[int] = None
self.criticalTemperatureTime: Optional[int] = None
self.errors: List[NvmeError] = []
self.tests: List[NvmeSelfTest] = []
if data is not None:
self.parse(data)
def parse(self, data: Iterator[str]) -> None:
"""Parses the attributes from the raw data
"""
# Advance data until detect Nvme Log
for line in data:
# Smart section: 'SMART/Health Information (NVMe Log 0x02)'
if line.startswith('SMART/Health Information (NVMe Log 0x02)'):
# Parse attributes
for line in data:
line = line.strip()
if not line or len(line) == 0:
break
# Parse attribute
match = re.match(
r'^\s*(?P<name>.+)\s*:\s*(?P<value>.+)\s*$', line)
if match:
name = match.group('name')
value = match.group('value')
if name == 'Critical Warning':
self.criticalWarning = int(value, 16)
elif name == 'Temperature':
# Check if temperature is in Celsius or Fahrenheit
if value.endswith('Celsius'):
self._temperature = int(value[:-7])
elif value.endswith('Fahrenheit'):
self._temperature = int(
(int(value[:-10]) - 32) / 1.8)
elif name == 'Available Spare':
self.availableSpare = int(value[:-1])
elif name == 'Available Spare Threshold':
self.availableSpareThreshold = int(value[:-1])
elif name == 'Percentage Used':
self.percentageUsed = int(value[:-1])
elif name == 'Data Units Read':
# Format: 1,234,567 [2.00 TB]
# Or : 0
if value.isdigit():
self.dataUnitsRead = int(value)
self.bytesRead = int(value)
else:
self.dataUnitsRead = int(
value.split(' ')[0].replace(',', '').replace('.', '').replace('’', ''))
self.bytesRead = humanfriendly.parse_size(
value.split(' ', 1)[1][1:-1].replace(',', '.'))
elif name == 'Data Units Written':
# Format: 1,234,567 [2.00 TB]
# Or : 0
if value.isdigit():
self.dataUnitsWritten = int(value)
self.bytesWritten = int(value)
else:
self.dataUnitsWritten = int(
value.split(' ')[0].replace(',', '').replace('.', '').replace('’', ''))
self.bytesWritten = humanfriendly.parse_size(
value.split(' ', 1)[1][1:-1].replace(',', '.'))
elif name == 'Host Read Commands':
self.hostReadCommands = int(
value.replace(',', '').replace('.', '').replace('’', ''))
elif name == 'Host Write Commands':
self.hostWriteCommands = int(
value.replace(',', '').replace('.', '').replace('’', ''))
elif name == 'Controller Busy Time':
self.controllerBusyTime = int(
value.replace(',', '').replace('.', ''))
elif name == 'Power Cycles':
self.powerCycles = int(
value.replace(',', '').replace('.', ''))
elif name == 'Power On Hours':
self.powerOnHours = int(
value.replace(',', '').replace('.', '').replace('’', ''))
elif name == 'Unsafe Shutdowns':
self.unsafeShutdowns = int(
value.replace(',', '').replace('.', ''))
elif name == 'Media and Data Integrity Errors':
self.integrityErrors = int(
value.replace(',', '').replace('.', ''))
elif name == 'Error Information Log Entries':
self.errorEntries = int(
value.replace(',', '').replace('.', ''))
elif name == 'Warning Comp. Temperature Time':
self.warningTemperatureTime = int(
value.replace(',', '').replace('.', ''))
elif name == 'Critical Comp. Temperature Time':
self.criticalTemperatureTime = int(
value.replace(',', '').replace('.', ''))
# Smart section: Error Information (NVMe Log 0x01, <num_entries> of <max_entries> entries)
elif line.startswith('Error Information (NVMe Log 0x01, '):
# check next line is:
# Num ErrCount SQId CmdId Status PELoc LBA NSID VS
# but be careful with the spaces
line = next(data)
if not re.match(r'^\s*Num\s+ErrCount\s+SQId\s+CmdId\s+Status\s+PELoc\s+LBA\s+NSID\s+VS\s*$', line):
continue
# Parse errors
for line in data:
line = line.strip()
if not line or len(line) == 0:
break
# Parse error
# Format: Num ErrCount SQId CmdId Status PELoc LBA NSID VS
# example 1: 0 1356 0 0x0012 0xc005 0x028 - 0 -
# example 2: 3 1 3 0x0045 0xc006 0x049 56 3 2
match = re.match(
r'^\s*(?P<num>\d+)\s+(?P<errCount>\d+)\s+(?P<sqId>\d+)\s+(?P<cmdId>\w+)\s+(?P<status>\w+)\s+(?P<peLoc>\w+)\s+(?P<lba>\S+)\s+(?P<nsid>\S+)\s+(?P<vs>\S+)\s*$', line)
if match:
error = NvmeError(
num=int(match.group('num')),
errCount=int(match.group('errCount')),
sqId=int(match.group('sqId')),
cmdId=int(match.group('cmdId'), 16),
status=int(match.group('status'), 16),
peLoc=int(match.group('peLoc'), 16)
)
if match.group('lba') != '-':
error.lba = int(match.group('lba'), 16)
if match.group('nsid') != '-':
error.nsid = int(match.group('nsid'))
if match.group('vs') != '-':
error.vs = int(match.group('vs'), 16)
self.errors.append(error)
elif line.startswith('Self-test Log (NVMe Log 0x06)'):
## NVME FORMAT ##
# Example smartctl output
# Self-test Log (NVMe Log 0x06)
# Self-test status: Extended self-test in progress (28% completed)
# Num Test_Description Status Power_on_Hours Failing_LBA NSID SCT Code
# 0 Extended Completed without error 3441 - - - -
nvme_entry_regex = re.compile(
r'^[#\s]*(\d+)\s{2,}(.*[^\s])\s{2,}(.*[^\s])\s{2,}(\d+)\s{2,}(.*[^\s])\s{2,}(.*[^\s])\s{2,}(.*[^\s])\s{2,}(.*)$')
line = next(data)
# Check the current test
# Example of non running test: Self-test status: No self-test in progress
# Example of running test: Self-test status: Extended self-test in progress (4% completed)
currentTest = None
if line.startswith('Self-test status:'):
line = line[18:].strip()
# check
if line.startswith('No self-test in progress'):
pass
else:
# parse the test
match = re.match(
r'^(\w+) self-test in progress \((\d+)% completed\)$', line)
if match:
powerOnHours = self.powerOnHours
if powerOnHours is None:
powerOnHours = 0
currentTest = NvmeSelfTest(
num=-1,
description=match.group(1),
status='Running',
powerOnHours=powerOnHours,
progress=int(match.group(2))
)
self.tests.append(currentTest)
# Parse tests
for line in data:
line = line.strip()
match = nvme_entry_regex.match(line)
if match:
num = int(match.group(1))
description = match.group(2)
status = match.group(3)
powerOnHours = int(match.group(4))
failingLBA = None
if match.group(5) != '-':
failingLBA = int(match.group(5))
nsid = None
if match.group(6) != '-':
nsid = int(match.group(6))
sct = match.group(7)
code = match.group(8)
test = NvmeSelfTest(
num=num,
description=description,
status=status,
powerOnHours=powerOnHours,
failingLBA=failingLBA,
nsid=nsid,
sct=sct,
code=code
)
self.tests.append(test)
def __getstate__(self):
"""
Allows us to send a pySMART diagnostics object over a serializable
medium which uses json (or the likes of json) payloads
"""
import copy
ret = copy.copy(vars(self))
if ret['errors'] is not None:
ret['errors'] = [e.__getstate__() for e in ret['errors']]
if ret['tests'] is not None:
ret['tests'] = [e.__getstate__() for e in ret['tests']]
return ret
def __setstate__(self, state):
self.__dict__.update(state)
@property
def temperature(self) -> Optional[int]:
return self._temperature