11/*
2- * Copyright 2020-2024 Hewlett Packard Enterprise Development LP
2+ * Copyright 2020-2025 Hewlett Packard Enterprise Development LP
33 * Other additional copyright holders may be indicated within.
44 *
55 * The entirety of this work is licensed under the Apache License,
@@ -364,6 +364,7 @@ func (s *Storage) initialize() error {
364364
365365 ctrl , err := s .device .IdentifyController (0 )
366366 if err != nil {
367+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
367368 return fmt .Errorf ("Initialize Storage %s: Failed to identify common controller: Error: %w" , s .id , err )
368369 }
369370
@@ -420,6 +421,8 @@ func (s *Storage) initialize() error {
420421 ns , err = s .device .IdentifyNamespace (CommonNamespaceIdentifier )
421422 if err != nil {
422423 if retryCount >= 2 {
424+ // After retries, this is likely a system-level failure
425+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
423426 return fmt .Errorf ("Initialize Storage %s: Failed to identify common namespace, retried %d times: Error: %w" , s .id , retryCount , err )
424427 }
425428
@@ -464,11 +467,19 @@ func (s *Storage) purge() error {
464467
465468 namespaces , err := s .device .ListNamespaces (0 )
466469 if err != nil {
470+ // System-level failure when listing namespaces
471+ if isSystemLevelError (err ) {
472+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
473+ }
467474 return err
468475 }
469476
470477 for _ , nsid := range namespaces {
471478 if err := s .device .DeleteNamespace (nsid ); err != nil {
479+ // System-level failure when deleting namespace during purge
480+ if isSystemLevelError (err ) {
481+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
482+ }
472483 return err
473484 }
474485 }
@@ -540,19 +551,23 @@ func (s *Storage) createVolume(desiredCapacityInBytes uint64) (*Volume, error) {
540551 actualCapacityBytes := roundUpToMultiple (desiredCapacityInBytes , s .blockSizeBytes )
541552
542553 s .log .V (2 ).Info ("Creating namespace" , "capacityInBytes" , actualCapacityBytes , "formatIndex" , s .lbaFormatIndex )
543- namespaceId , guid , err := s .device .CreateNamespace (actualCapacityBytes / s .blockSizeBytes , s .lbaFormatIndex )
554+ namespaceID , guid , err := s .device .CreateNamespace (actualCapacityBytes / s .blockSizeBytes , s .lbaFormatIndex )
544555 if err != nil {
556+ // Only publish error events for system-level failures
557+ if isSystemLevelError (err ) {
558+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
559+ }
545560 return nil , err
546561 }
547562
548- id := strconv .Itoa (int (namespaceId ))
563+ id := strconv .Itoa (int (namespaceID ))
549564
550- log := s .log .WithName (id ).WithValues (namespaceIdKey , namespaceId )
565+ log := s .log .WithName (id ).WithValues (namespaceIdKey , namespaceID )
551566 log .V (1 ).Info ("Created namespace" )
552567
553568 volume := Volume {
554569 id : id ,
555- namespaceId : namespaceId ,
570+ namespaceId : namespaceID ,
556571 guid : guid ,
557572 capacityBytes : actualCapacityBytes ,
558573 storage : s ,
@@ -575,6 +590,10 @@ func (s *Storage) deleteVolume(volumeId string) error {
575590 log .V (2 ).Info ("Deleting namespace" )
576591 err := s .device .DeleteNamespace (volume .namespaceId )
577592 if err != nil {
593+ // Only publish error events for system-level failures
594+ if isSystemLevelError (err ) {
595+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
596+ }
578597 log .Error (err , "Delete namespace failed, removing from in-memory state anyway" , "capacity" , volume .capacityBytes )
579598 } else {
580599 log .V (1 ).Info ("Deleted namespace" , "capacity" , volume .capacityBytes )
@@ -599,6 +618,10 @@ func (s *Storage) formatVolume(volumeID string) error {
599618
600619 log .V (2 ).Info ("Format namespace" )
601620 if err := s .device .FormatNamespace (volume .namespaceId ); err != nil {
621+ // Only publish error events for system-level failures
622+ if isSystemLevelError (err ) {
623+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
624+ }
602625 log .Error (err , "Format namespace failure" )
603626 return err
604627 }
@@ -614,6 +637,10 @@ func (s *Storage) formatVolume(volumeID string) error {
614637func (s * Storage ) recoverStorageVolumes () error {
615638 namespaces , err := s .device .ListNamespaces (0 )
616639 if err != nil {
640+ // Only publish error events for system-level failures
641+ if isSystemLevelError (err ) {
642+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
643+ }
617644 s .log .Error (err , "Failed to list device namespaces" )
618645 }
619646
@@ -623,6 +650,10 @@ func (s *Storage) recoverStorageVolumes() error {
623650
624651 ns , err := s .device .IdentifyNamespace (nsid )
625652 if err != nil {
653+ // Only publish error events for system-level failures
654+ if isSystemLevelError (err ) {
655+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
656+ }
626657 log .Error (err , "Failed to identify namespace" )
627658 continue
628659 }
@@ -649,6 +680,66 @@ func (s *Storage) recoverStorageVolumes() error {
649680 return nil
650681}
651682
683+ // isSystemLevelError determines if an error represents a system-level failure
684+ // (hardware, communication, etc.) rather than an expected operational failure
685+ // (insufficient capacity, namespace already exists, etc.).
686+ // System-level errors should trigger ResourceState event publishing.
687+ func isSystemLevelError (err error ) bool {
688+ if err == nil {
689+ return false
690+ }
691+
692+ // Check if it's an NVMe command error with a specific status code
693+ var cmdErr * nvme.CommandError
694+ if errors .As (err , & cmdErr ) {
695+ switch cmdErr .StatusCode {
696+ // Expected operational failures - don't publish events
697+ case nvme .NamespaceAlreadyAttached :
698+ return false
699+ case nvme .NamespaceNotAttached :
700+ return false
701+ case 0x0A : // Namespace Insufficient Capacity
702+ return false
703+ case 0x0B : // Namespace Identifier Unavailable
704+ return false
705+ case 0x0C : // Namespace Already Exists
706+ return false
707+ case 0x15 : // Invalid Namespace Identifier (operational - bad request)
708+ return false
709+ case 0x02 : // Invalid Field in Command (operational - bad parameters)
710+ return false
711+ default :
712+ // Unknown status codes are considered system-level errors
713+ // This includes hardware failures, communication timeouts, etc.
714+ return true
715+ }
716+ }
717+
718+ // Check for mock device operational errors
719+ errMsg := err .Error ()
720+ if strings .Contains (errMsg , "Insufficient capacity" ) ||
721+ strings .Contains (errMsg , "Could not find free namespace" ) ||
722+ strings .Contains (errMsg , "Namespace Already Exists" ) ||
723+ strings .Contains (errMsg , "already has controller" ) ||
724+ strings .Contains (errMsg , "already has controller" ) ||
725+ strings .Contains (errMsg , "not found" ) {
726+ return false
727+ }
728+
729+ // Check for CLI parsing errors (not system-level)
730+ if strings .Contains (errMsg , "NSID not found in response output" ) {
731+ return false
732+ }
733+
734+ // All other errors are considered system-level failures:
735+ // - Device communication errors
736+ // - Hardware failures
737+ // - Timeout errors
738+ // - Permission/access errors
739+ // - Unknown NVMe status codes
740+ return true
741+ }
742+
652743func (s * Storage ) findVolume (volumeId string ) * Volume {
653744 for idx , v := range s .volumes {
654745 if v .id == volumeId {
@@ -727,6 +818,10 @@ func (v *Volume) WaitFormatComplete() error {
727818 log .V (2 ).Info ("Wait for format completion" )
728819 ns , err := v .storage .device .IdentifyNamespace (v .namespaceId )
729820 if err != nil {
821+ // System-level failure when identifying namespace during format wait
822+ if isSystemLevelError (err ) {
823+ v .storage .notify (sf .UNAVAILABLE_OFFLINE_RST )
824+ }
730825 return err
731826 }
732827 if ns == nil {
@@ -746,6 +841,10 @@ func (v *Volume) WaitFormatComplete() error {
746841
747842 ns , err = v .storage .device .IdentifyNamespace (v .namespaceId )
748843 if err != nil {
844+ // System-level failure when re-identifying namespace during format wait
845+ if isSystemLevelError (err ) {
846+ v .storage .notify (sf .UNAVAILABLE_OFFLINE_RST )
847+ }
749848 return err
750849 }
751850 if ns == nil {
@@ -808,9 +907,17 @@ func (v *Volume) attach(controllerIndex uint16) error {
808907 var cmdErr * nvme.CommandError
809908 if errors .As (err , & cmdErr ) {
810909 if cmdErr .StatusCode != nvme .NamespaceAlreadyAttached {
910+ // Only publish error events for system-level failures
911+ if isSystemLevelError (err ) {
912+ v .storage .notify (sf .UNAVAILABLE_OFFLINE_RST )
913+ }
811914 return err
812915 }
813916 } else {
917+ // Non-CommandError types - check if system-level
918+ if isSystemLevelError (err ) {
919+ v .storage .notify (sf .UNAVAILABLE_OFFLINE_RST )
920+ }
814921 return err
815922 }
816923 }
@@ -834,9 +941,17 @@ func (v *Volume) detach(controllerIndex uint16) error {
834941 var cmdErr * nvme.CommandError
835942 if errors .As (err , & cmdErr ) {
836943 if cmdErr .StatusCode != nvme .NamespaceNotAttached {
944+ // Only publish error events for system-level failures
945+ if isSystemLevelError (err ) {
946+ v .storage .notify (sf .UNAVAILABLE_OFFLINE_RST )
947+ }
837948 return err
838949 }
839950 } else {
951+ // Non-CommandError types - check if system-level
952+ if isSystemLevelError (err ) {
953+ v .storage .notify (sf .UNAVAILABLE_OFFLINE_RST )
954+ }
840955 return err
841956 }
842957 }
@@ -866,6 +981,10 @@ func (v *Volume) AttachControllerIfUnattached(controllerIndex uint16) error {
866981 attachedControllers , err := v .listAttachedControllers ()
867982 if err != nil {
868983 log .Error (err , "failed to list attached controllers" )
984+ // System-level failure when listing attached controllers
985+ if isSystemLevelError (err ) {
986+ v .storage .notify (sf .UNAVAILABLE_OFFLINE_RST )
987+ }
869988 return err
870989 }
871990
@@ -1332,6 +1451,10 @@ func (mgr *Manager) StorageIdVolumeIdGet(storageId, volumeId string, model *sf.V
13321451 ns , err := s .device .IdentifyNamespace (nvme .NamespaceIdentifier (v .namespaceId ))
13331452 if err != nil {
13341453 v .log .Error (err , "Identify Namespace Failed" )
1454+ // System-level failure when identifying namespace for volume retrieval
1455+ if isSystemLevelError (err ) {
1456+ s .notify (sf .UNAVAILABLE_OFFLINE_RST )
1457+ }
13351458 return ec .NewErrInternalServerError ()
13361459 }
13371460 if ns == nil {
0 commit comments