diff --git a/build_images.sh b/build_images.sh index b3005f54e..0be46a3ac 100755 --- a/build_images.sh +++ b/build_images.sh @@ -72,7 +72,7 @@ eval_cmd_exec "/usr/bin/docker build -f install/Docker/dockerfiles/Dockerfile.sy eval_cmd_exec "/usr/bin/docker build -f install/Docker/dockerfiles/Dockerfile.task -t task:6.0 ." "task" eval_cmd_exec "/usr/bin/docker build -f install/Docker/dockerfiles/Dockerfile.telemetry -t telemetry:4.0 ." "telemetry" eval_cmd_exec "/usr/bin/docker build -f install/Docker/dockerfiles/Dockerfile.update -t update:6.0 ." "update" -#eval_cmd_exec "/usr/bin/docker build -f install/Docker/dockerfiles/Dockerfile.compositionService -t composition-service:1.0 ." "composition service" +# eval_cmd_exec "/usr/bin/docker build -f install/Docker/dockerfiles/Dockerfile.compositionService -t composition-service:1.0 ." "composition service" # ODIMRA plugins image eval_cmd_exec "/usr/bin/docker build -f install/Docker/dockerfiles/Dockerfile.urplugin -t urplugin:3.4 ." "urplugin" diff --git a/lib-dmtf/model/Chassis.go b/lib-dmtf/model/Chassis.go index 84520895a..753f5ed16 100644 --- a/lib-dmtf/model/Chassis.go +++ b/lib-dmtf/model/Chassis.go @@ -21,65 +21,98 @@ import ( // Chassis redfish structure type Chassis struct { - Ocontext string `json:"@odata.context,omitempty"` - Oid string `json:"@odata.id"` - Otype string `json:"@odata.type"` - Oetag string `json:"@odata.etag,omitempty"` - ID string `json:"Id"` - Description string `json:"Description,omitempty"` - Name string `json:"Name"` - AssetTag interface{} `json:"AssetTag"` // omitempty is not added to make value as null if it's not present - ChassisType string `json:"ChassisType"` - DepthMm float32 `json:"DepthMm,omitempty"` - EnvironmentalClass string `json:"EnvironmentalClass,omitempty"` - HeightMm float32 `json:"HeightMm,omitempty"` - IndicatorLED string `json:"IndicatorLED,omitempty"` - Manufacturer string `json:"Manufacturer,omitempty"` - Model string `json:"Model,omitempty"` - PartNumber interface{} `json:"PartNumber"` // omitempty is not added to make value as null if it's not present - PowerState string `json:"PowerState,omitempty"` - SerialNumber string `json:"SerialNumber,omitempty"` - SKU string `json:"SKU,omitempty"` - UUID string `json:"UUID,omitempty"` - WeightKg float32 `json:"WeightKg,omitempty"` - WidthMm float32 `json:"WidthMm,omitempty"` - Links *Links `json:"Links,omitempty"` - Location *Link `json:"Location,omitempty"` - LogServices *LogServices `json:"LogServices,omitempty"` - Assembly *Assembly `json:"Assembly,omitempty"` - NetworkAdapters *NetworkAdapters `json:"NetworkAdapters,omitempty"` - PCIeSlots *PCIeSlots `json:"PCIeSlots,omitempty"` - PhysicalSecurity *PhysicalSecurity `json:"PhysicalSecurity,omitempty"` - Power *Link `json:"Power,omitempty"` - Sensors *Sensors `json:"Sensors,omitempty"` - Status *Status `json:"Status,omitempty"` - Thermal *Link `json:"Thermal,omitempty"` - Actions *OemActions `json:"Actions,omitempty"` - Certificates *Certificates `json:"Certificates,omitempty"` - Controls *Link `json:"Controls,omitempty"` - Drives *Link `json:"Drives,omitempty"` - EnvironmentMetrics *Link `json:"EnvironmentMetrics,omitempty"` - LocationIndicatorActive bool `json:"LocationIndicatorActive,omitempty"` - MaxPowerWatts float32 `json:"MaxPowerWatts,omitempty"` - Measurements []*Link `json:"Measurements,omitempty"` // Deprecated in version v1.19.0 - MediaControllers *Link `json:"MediaControllers,omitempty"` - Memory *Link `json:"Memory,omitempty"` - MemoryDomains *Link `json:"MemoryDomains,omitempty"` - MinPowerWatts float32 `json:"MinPowerWatts,omitempty"` - Oem *Oem `json:"Oem,omitempty"` - PCIeDevices *Link `json:"PCIeDevices,omitempty"` - PowerSubsystem *Link `json:"PowerSubsystem,omitempty"` - SparePartNumber string `json:"SparePartNumber,omitempty"` - ThermalSubsystem *Link `json:"ThermalSubsystem,omitempty"` - ThermalDirection string `json:"ThermalDirection,omitempty"` - ThermalManagedByParent bool `json:"ThermalManagedByParent,omitempty"` - PoweredByParent bool `json:"PoweredByParent,omitempty"` - Fans []*Link `json:"Fans,omitempty"` - PowerSupplies []*Link `json:"PowerSupplies,omitempty"` - PowerDistribution *Link `json:"PowerDistribution,omitempty"` - FabricAdapters *Link `json:"FabricAdapters,omitempty"` - ElectricalSourceManagerURIs []string `json:"ElectricalSourceManagerURIs,omitempty"` - ElectricalSourceNames []string `json:"ElectricalSourceNames,omitempty"` + Ocontext string `json:"@odata.context,omitempty"` + Oid string `json:"@odata.id"` + Otype string `json:"@odata.type"` + Oetag string `json:"@odata.etag,omitempty"` + ID string `json:"Id"` + Description string `json:"Description,omitempty"` + Name string `json:"Name"` + AssetTag interface{} `json:"AssetTag"` // omitempty is not added to make value as null if it's not present + ChassisType string `json:"ChassisType"` + DepthMm float32 `json:"DepthMm,omitempty"` + EnvironmentalClass string `json:"EnvironmentalClass,omitempty"` + HeightMm float32 `json:"HeightMm,omitempty"` + IndicatorLED string `json:"IndicatorLED,omitempty"` + Manufacturer string `json:"Manufacturer,omitempty"` + Model string `json:"Model,omitempty"` + PartNumber interface{} `json:"PartNumber"` // omitempty is not added to make value as null if it's not present + PowerState string `json:"PowerState,omitempty"` + SerialNumber string `json:"SerialNumber,omitempty"` + SKU string `json:"SKU,omitempty"` + UUID string `json:"UUID,omitempty"` + WeightKg float32 `json:"WeightKg,omitempty"` + WidthMm float32 `json:"WidthMm,omitempty"` + Links *Links `json:"Links,omitempty"` + Location *Link `json:"Location,omitempty"` + LogServices *LogServices `json:"LogServices,omitempty"` + Assembly *Assembly `json:"Assembly,omitempty"` + NetworkAdapters *NetworkAdapters `json:"NetworkAdapters,omitempty"` + PCIeSlots *PCIeSlots `json:"PCIeSlots,omitempty"` + PhysicalSecurity *PhysicalSecurity `json:"PhysicalSecurity,omitempty"` + Power *Link `json:"Power,omitempty"` + Sensors *Sensors `json:"Sensors,omitempty"` + Status *Status `json:"Status,omitempty"` + Thermal *Link `json:"Thermal,omitempty"` + Actions *OemActions `json:"Actions,omitempty"` + Certificates *Certificates `json:"Certificates,omitempty"` + Controls *Link `json:"Controls,omitempty"` + Drives *Link `json:"Drives,omitempty"` + EnvironmentMetrics *Link `json:"EnvironmentMetrics,omitempty"` + LocationIndicatorActive bool `json:"LocationIndicatorActive,omitempty"` + MaxPowerWatts float32 `json:"MaxPowerWatts,omitempty"` + Measurements []*Link `json:"Measurements,omitempty"` // Deprecated in version v1.19.0 + MediaControllers *Link `json:"MediaControllers,omitempty"` + Memory *Link `json:"Memory,omitempty"` + MemoryDomains *Link `json:"MemoryDomains,omitempty"` + MinPowerWatts float32 `json:"MinPowerWatts,omitempty"` + Oem *Oem `json:"Oem,omitempty"` + PCIeDevices *Link `json:"PCIeDevices,omitempty"` + PowerSubsystem *Link `json:"PowerSubsystem,omitempty"` + SparePartNumber string `json:"SparePartNumber,omitempty"` + ThermalSubsystem *Link `json:"ThermalSubsystem,omitempty"` + ThermalDirection string `json:"ThermalDirection,omitempty"` + ThermalManagedByParent bool `json:"ThermalManagedByParent,omitempty"` + PoweredByParent bool `json:"PoweredByParent,omitempty"` + Fans []*Link `json:"Fans,omitempty"` + PowerSupplies []*Link `json:"PowerSupplies,omitempty"` + PowerDistribution *Link `json:"PowerDistribution,omitempty"` + FabricAdapters *Link `json:"FabricAdapters,omitempty"` + ElectricalSourceManagerURIs []string `json:"ElectricalSourceManagerURIs,omitempty"` + ElectricalSourceNames []string `json:"ElectricalSourceNames,omitempty"` + HotPluggable bool `json:"HotPluggable,omitempty"` + TrustedComponents *TrustedComponentCollection `json:"TrustedComponents,omitempty"` + Version string `json:"Version,omitempty"` + Processors ProcessorCollection `json:"Processors,omitempty"` + Replaceable bool `json:"Replaceable,omitempty"` +} + +// TrustedComponentCollection redfish structure +type TrustedComponentCollection struct { + Oid string `json:"@odata.id"` + Ocontext string `json:"@odata.context,omitempty"` + Otype string `json:"@odata.type"` + Oetag string `json:"@odata.etag,omitempty"` + Description string `json:"Description,omitempty"` + Name string `json:"Name"` + Oem *Oem `json:"Oem,omitempty"` + Members []string `json:"Members"` + MembersODataCount int `json:"Members@odata.count"` + MembersODataNextLink string `json:"Members@odata.nextLink,omitempty"` +} + +// ProcessorCollection redfish structure +type ProcessorCollection struct { + Oid string `json:"@odata.id"` + Ocontext string `json:"@odata.context,omitempty"` + Otype string `json:"@odata.type"` + Oetag string `json:"@odata.etag,omitempty"` + Description string `json:"Description,omitempty"` + Name string `json:"Name"` + Oem *Oem `json:"Oem,omitempty"` + Members []string `json:"Members"` + MembersODataCount int `json:"Members@odata.count"` + MembersODataNextLink string `json:"Members@odata.nextLink,omitempty"` } // LogServices get diff --git a/lib-dmtf/model/ComputerSystem.go b/lib-dmtf/model/ComputerSystem.go index 86f78216f..b44743cb2 100644 --- a/lib-dmtf/model/ComputerSystem.go +++ b/lib-dmtf/model/ComputerSystem.go @@ -81,6 +81,8 @@ type IndicatorLED string // BootProgressTypes - The last boot progress state type BootProgressTypes string +type ConnectedTypesSupported string + const ( // BootSourceNone - Boot from the normal boot device @@ -369,6 +371,10 @@ const ( //IndicatorLEDOff - The indicator LED is off IndicatorLEDOff IndicatorLED = "Off" + + ConnectedTypesSupportedKVMIP ConnectedTypesSupported = "KVMIP" + + ConnectedTypesSupportedOEM ConnectedTypesSupported = "OEM" ) // AddResourceBlock redfish structure @@ -459,6 +465,20 @@ type ComputerSystem struct { Composition *Composition `json:"Composition,omitempty"` } +type HostGraphicalConsole struct { + ConnectedTypesSupported []string `json:"ConnectedTypesSupported,omitempty"` //enum + MaxConcurrentSessions int `json:"MaxConcurrentSessions,omitempty"` + Port int `json:"Port,omitempty"` + ServiceEnabled bool `json:"ServiceEnabled,omitempty"` +} + +type HostSerialConsole struct { + IPMI *SerialConsoleProtocol `json:"IPMI,omitempty"` + MaxConcurrentSessions int `json:"MaxConcurrentSessions,omitempty"` + SSH *SerialConsoleProtocol `json:"SSH,omitempty"` + Telnet *SerialConsoleProtocol `json:"Telnet,omitempty"` +} + // ComputerSystemActions redfish structure type ComputerSystemActions struct { AddResourceBlock *AddResourceBlock `json:"AddResourceBlock,omitempty"` @@ -1140,7 +1160,7 @@ type Processors struct { HighSpeedCoreIDs []int `json:"HighSpeedCoreIDs,omitempty"` LocationIndicatorActive bool `json:"LocationIndicatorActive,omitempty"` Measurements []*Link `json:"Measurements,omitempty"` - MemorySummary *MemorySummaryDetails `json:"MemorySummary,omitempty"` + MemorySummary *MemorySummary `json:"MemorySummary,omitempty"` MinSpeedMHz int `json:"MinSpeedMHz,omitempty"` OperatingConfigs *Link `json:"OperatingConfigs,omitempty"` OperatingSpeedMHz int `json:"OperatingSpeedMHz,omitempty"` @@ -1155,6 +1175,28 @@ type Processors struct { AdditionalFirmwareVersions *AdditionalFirmwareVersions `json:"AdditionalFirmwareVersions,omitempty"` } +// SystemInterface redfish structure +type MemoryMetrics struct { + Oid string `json:"@odata.id"` + Ocontext string `json:"@odata.context,omitempty"` + Oetag string `json:"@odata.etag,omitempty"` + Otype string `json:"@odata.type"` + Actions *OemActions `json:"Actions,omitempty"` + BandwidthPercent int `json:"BandwidthPercent,omitempty"` + BlockSizeBytes int `json:"BlockSizeBytes,omitempty"` + CXL CXL `json:"CXL,omitempty"` + CapacityUtilizationPercent int `json:"CapacityUtilizationPercent,omitempty"` + CorrectedPersistentErrorCount int `json:"CorrectedPersistentErrorCount,omitempty"` + CorrectedVolatileErrorCount int `json:"CorrectedVolatileErrorCount,omitempty"` + CurrentPeriod *CurrentPeriod `json:"CurrentPeriod,omitempty"` + Description string `json:"description,omitempty"` + DirtyShutdownCount int `json:"DirtyShutdownCount,omitempty"` + ID string `json:"ID"` + Name string `json:"Name"` + Oem Oem `json:"Oem,omitempty"` + OperatingSpeedMHz int `json:"OperatingSpeedMHz,omitempty"` +} + // SystemInterface redfish structure type SystemInterface struct { Ethernet Ethernet `json:"Ethernet,omitempty"` diff --git a/lib-dmtf/model/Links.go b/lib-dmtf/model/Links.go index 6072f1ce2..86dfe900d 100644 --- a/lib-dmtf/model/Links.go +++ b/lib-dmtf/model/Links.go @@ -43,6 +43,7 @@ type Links struct { NetworkDeviceFunctions []*Link `json:"NetworkDeviceFunctions,omitempty"` PowerOutlets []*Link `json:"PowerOutlets,omitempty"` DedicatedSpareDrives []*Link `json:"DedicatedSpareDrives,omitempty"` + NVMeoDiscoverySubsystems []*Link `json:"NVMeoDiscoverySubsystems,omitempty"` } // Link holds the odata id redfish links diff --git a/lib-dmtf/model/accountService.go b/lib-dmtf/model/accountService.go index 244beebae..ee993b0c7 100644 --- a/lib-dmtf/model/accountService.go +++ b/lib-dmtf/model/accountService.go @@ -292,9 +292,11 @@ type ExternalAccountProvider struct { PasswordSet bool `json:"PasswordSet,omitempty"` Priority int `json:"Priority,omitempty"` RemoteRoleMapping []*RoleMapping `json:"RemoteRoleMapping,omitempty"` + Retries int `json:"Retries,omitempty"` ServiceAddresses []string `json:"ServiceAddresses,omitempty"` ServiceEnabled bool `json:"ServiceEnabled,omitempty"` TACACSplusService *TACACSplusService `json:"TACACSplusService,omitempty"` + TimeoutSeconds int `json:"TimeoutSeconds,omitempty"` } // GoogleAuthenticator redfish structure @@ -359,6 +361,7 @@ type OAuth2Service struct { Issuer string `json:"Issuer,omitempty"` Mode string `json:"Mode,omitempty"` //enum OAuthServiceSigningKeys string `json:"OAuthServiceSigningKeys,omitempty"` + Oem *Oem `json:"Oem,omitempty"` } // SecurID redfish structure @@ -390,6 +393,7 @@ type RoleMapping struct { // Various settings to parse a TACACS+ service // This type shall contain settings for parsing a TACACS+ service type TACACSplusService struct { + AuthorizationService string `json:"AuthorizationService,omitempty"` PasswordExchangeProtocols string `json:"PasswordExchangeProtocols,omitempty"` //enum PrivilegeLevelArgument string `json:"PrivilegeLevelArgument,omitempty"` } diff --git a/lib-dmtf/model/endpoint.go b/lib-dmtf/model/endpoint.go index 4a9613f85..41ca65db3 100644 --- a/lib-dmtf/model/endpoint.go +++ b/lib-dmtf/model/endpoint.go @@ -86,6 +86,12 @@ type PciID struct { VendorID string `json:"VendorId,omitempty"` } +// GCID for Endpoint +type GCID struct { + CID string `json:"GCID,omitempty"` + SID string `json:"SID,omitemoty"` +} + // EndpointLinks is the struct to links under a AddressPool type EndpointLinks struct { AddressPools []Link `json:"AddressPools,omitempty"` diff --git a/lib-dmtf/model/events.go b/lib-dmtf/model/events.go index 1f867eea8..7ff3615c8 100644 --- a/lib-dmtf/model/events.go +++ b/lib-dmtf/model/events.go @@ -62,6 +62,8 @@ type SMTPAuthentication string // SMTPConnectionProtocol - This property shall contain the connection type to the outgoing SMTP server. type SMTPConnectionProtocol string +type DiagnosticDataType string + const ( // EventTypeAlert - "Alert": "A condition requires attention." EventTypeAlert EventType = "Alert" @@ -297,8 +299,33 @@ const ( // SMTPConnectionProtocolTLSSSL - SMTPConnection Protocol type TLS_SSL SMTPConnectionProtocolTLSSSL SMTPConnectionProtocol = "TLS_SSL" + + //Manager diagnostic data. + DiagnosticDataTypeManager DiagnosticDataType = "Manager" + + //Pre-OS diagnostic data. + DiagnosticDataTypePreOS DiagnosticDataType = "PreOS" + + //Operating system (OS) diagnostic data. + DiagnosticDataTypeOS DiagnosticDataType = "OS" + + //OEM diagnostic data. + DiagnosticDataTypeOEM DiagnosticDataType = "OEM" + + //UEFI Common Platform Error Record. + DiagnosticDataTypeCPER DiagnosticDataType = "CPER" + + // A Section of a UEFI Common Platform Error Record. + DiagnosticDataTypeCPERSection DiagnosticDataType = "CPERSection" ) +// Details for a CPER section or record associated with an event. +type CPER struct { + NotificationType string `json:"NotificationType,omitempty"` + Oem interface{} `json:"Oem,omitempty"` + SectionType string `json:"SectionType,omitempty"` +} + // Event schema describes the JSON payload received by an event destination, which has // subscribed to event notification, when events occur. This resource contains data // about events, including descriptions, severity, and a message identifier to a @@ -323,7 +350,12 @@ type Event struct { // Refer to Event.v1_7_0.json of the redfish spec for more details type EventRecord struct { Actions *OemActions `json:"Actions,omitempty"` + AdditionalDataSizeBytes int `json:"AdditionalDataSizeBytes,omitempty"` + AdditionalDataURI string `json:"AdditionalDataURI,omitempty"` + CPER CPER `json:"CPER,omitempty"` Context string `json:"Context,omitempty"` + DiagnosticData string `json:"DiagnosticData,omitempty"` + DiagnosticDataType string `json:"DiagnosticDataType,omitempty"` EventGroupID int `json:"EventGroupId,omitempty"` EventID string `json:"EventId,omitempty"` EventTimestamp string `json:"EventTimestamp,omitempty"` diff --git a/lib-dmtf/model/port.go b/lib-dmtf/model/port.go index e69a01073..bd5be7e56 100644 --- a/lib-dmtf/model/port.go +++ b/lib-dmtf/model/port.go @@ -14,6 +14,51 @@ package model +// The type of SFP device that is attached to this port. +type Type string + +const ( + + // The SFP conforms to the SFF Specification for SFP. + TypeSFP Type = "SFP" + + // The SFP conforms to the SFF Specification for SFP+. + TypeSFPPlus Type = "SFPPlus" + + // The SFP conforms to the SFF Specification for SFP+ and IEEE 802.3by Specification. + TypeSFP28 Type = "SFP28" + + // The SFP conforms to the CSFP MSA Specification. + TypecSFP Type = "cSFP" + + // The SFP conforms to the SFP-DD MSA Specification. + TypeSFPDD Type = "SFPDD" + + // The SFP conforms to the SFF Specification for QSFP. + TypeQSFP Type = "QSFP" + + // The SFP conforms to the SFF Specification for QSFP+. + TypeQSFPPlus Type = "QSFPPlus" + + // The SFP conforms to the SFF Specification for QSFP14. + TypeQSFP14 Type = "QSFP14" + + // The SFP conforms to the SFF Specification for QSFP28. + TypeQSFP28 Type = "QSFP28" + + // The SFP conforms to the SFF Specification for QSFP56. + TypeQSFP56 Type = "QSFP56" + + // The SFP conforms to the SFF Specification SFF-8644. + TypeMiniSASHD Type = "MiniSASHD" + + // The SFP conforms to the QSFP Double Density Specification. + TypeQSFPDD Type = "QSFPDD" + + // The SFP conforms to the OSFP Specification. + TypeOSFP Type = "OSFP" +) + // Port is the redfish Port model according to the 2020.3 release type Port struct { ODataContext string `json:"@odata.context,omitempty"` @@ -61,7 +106,7 @@ type Port struct { RemotePortId string `json:"RemotePortId,omitempty"` } -//InfiniBand redfish structure +// InfiniBand redfish structure type InfiniBand struct { AssociatedNodeGUIDs []string `json:"AssociatedNodeGUIDs,omitempty"` AssociatedPortGUIDs []string `json:"AssociatedPortGUIDs,omitempty"` @@ -77,6 +122,15 @@ type CXL struct { MaxLogicalDeviceCount string `json:"MaxLogicalDeviceCount,omitempty"` QoSTelemetryCapabilities *QoSTelemetryCapabilities `json:"QoSTelemetryCapabilities,omitempty"` TemporaryThroughputReductionEnabled bool `json:"TemporaryThroughputReductionEnabled,omitempty"` + AlertCapabilities *AlertCapabilities `json:"AlertCapabilities,omitempty"` +} + +// AlertCapabilities redfish structure +type AlertCapabilities struct { + CorrectableECCError bool `json:"CorrectableECCError,omitempty"` + SpareBlock bool `json:"SpareBlock,omitempty"` + Temperature bool `json:"Temperature,omitempty"` + UncorrectableECCError bool `json:"UncorrectableECCError,omitempty"` } // Congestion redfish structure diff --git a/lib-dmtf/model/resourceCollection.go b/lib-dmtf/model/resourceCollection.go index 781dade9d..5bf3eb001 100644 --- a/lib-dmtf/model/resourceCollection.go +++ b/lib-dmtf/model/resourceCollection.go @@ -16,16 +16,16 @@ package model // Collection is the redfish resource collection model according to the 2020.3 release type Collection struct { - ODataContext string `json:"@odata.context,omitempty"` - ODataEtag string `json:"@odata.etag,omitempty"` - ODataID string `json:"@odata.id"` - ODataType string `json:"@odata.type"` - Description string `json:"Description,omitempty"` - Name string `json:"Name"` - Members []*Link `json:"Members"` - MembersCount int `json:"Members@odata.count"` - MemberNavigationLink string `json:"Members@odata.navigationLink,omitempty"` - Oem interface{} `json:"Oem,omitempty"` - MembersNextLink string `json:"Members@odata.nextLink,omitempty"` - CollectionCapabilitie CollectionCapabilities `json:"@Redfish.CollectionCapabilities,omitempty"` + ODataContext string `json:"@odata.context,omitempty"` + ODataEtag string `json:"@odata.etag,omitempty"` + ODataID string `json:"@odata.id"` + ODataType string `json:"@odata.type"` + Description string `json:"Description,omitempty"` + Name string `json:"Name"` + Members []*Link `json:"Members"` + MembersCount int `json:"Members@odata.count"` + MemberNavigationLink string `json:"Members@odata.navigationLink,omitempty"` + Oem interface{} `json:"Oem,omitempty"` + MembersNextLink string `json:"Members@odata.nextLink,omitempty"` + CollectionCapabilitie *CollectionCapabilities `json:"@Redfish.CollectionCapabilities,omitempty"` } diff --git a/lib-dmtf/model/serviceRoot.go b/lib-dmtf/model/serviceRoot.go index e1b043ca2..caffeb91a 100644 --- a/lib-dmtf/model/serviceRoot.go +++ b/lib-dmtf/model/serviceRoot.go @@ -60,6 +60,55 @@ type ServiceRoot struct { RegisteredClients *Link `json:"RegisteredClients,omitempty"` ServiceConditions *Link `json:"ServiceConditions,omitempty"` LicenseService *Link `json:"LicenseService,omitempty"` + PowerEquipment PowerEquipment `json:"PowerEquipment,omitempty"` + ThermalEquipment ThermalEquipment `json:"ThermalEquipment,omitempty"` +} + +// ThermalEquipment redfish structure +type ThermalEquipment struct { + ODataContext string `json:"@odata.context,omitempty"` + ODataID string `json:"@odata.id,omitempty"` + ODataEtag string `json:"@odata.etag,omitempty"` + ODataType string `json:"@odata.type,omitempty"` + Description string `json:"Description,omitempty"` + Actions *OemActions `json:"Actions,omitempty"` + ID string `json:"Id"` + Name string `json:"Name"` + OEM *Oem `json:"Oem,omitempty"` + Status Status `json:"Status,omitempty"` + CDUs CoolingLoopCollection `json:"CDUs,omitempty"` + CoolingLoops CoolingLoopCollection `json:"CoolingLoops,omitempty"` + HeatExchangers CoolingLoopCollection `json:"HeatExchangers,omitempty"` + ImmersionUnits CoolingLoopCollection `json:"ImmersionUnits,omitempty"` +} + +// CoolingLoopCollection redfish structure +type CoolingLoopCollection struct { + ODataContext string `json:"@odata.context,omitempty"` + ODataID string `json:"@odata.id,omitempty"` + ODataEtag string `json:"@odata.etag,omitempty"` + ODataType string `json:"@odata.type,omitempty"` + Description string `json:"Description,omitempty"` + Name string `json:"Name"` + OEM *Oem `json:"Oem,omitempty"` + Members []string `json:"Members"` + MembersODataCount int `json:"Members@odata.count"` + MembersODataNextLink string `json:"Members@odata.nextLink,omitempty"` +} + +// PowerEquipment redfish structure +type PowerEquipment struct { + ODataContext string `json:"@odata.context,omitempty"` + ODataID string `json:"@odata.id,omitempty"` + ODataEtag string `json:"@odata.etag,omitempty"` + ODataType string `json:"@odata.type,omitempty"` + Description string `json:"Description,omitempty"` + Actions *OemActions `json:"Actions,omitempty"` + ID string `json:"Id"` + Links Links `json:"Links"` + Name string `json:"Name"` + Status Status `json:"Status,omitempty"` + OEM *Oem `json:"Oem,omitempty"` } // Product redfish structure @@ -67,11 +116,20 @@ type Product struct{} // ProtocolFeaturesSupported redfish structure type ProtocolFeaturesSupported struct { - ExcerptQuery bool `json:"ExcerptQuery"` - ExpandQuery *ExpandQuery `json:"ExpandQuery:omitempty"` - FilterQuery bool `json:"FilterQuery"` - OnlyMemberQuery bool `json:"OnlyMemberQuery"` - SelectQuery bool `json:"SelectQuery"` + ExcerptQuery bool `json:"ExcerptQuery"` + ExpandQuery *ExpandQuery `json:"ExpandQuery:omitempty"` + FilterQuery bool `json:"FilterQuery"` + OnlyMemberQuery bool `json:"OnlyMemberQuery"` + SelectQuery bool `json:"SelectQuery"` + MultipleHTTPRequests bool `json:"MultipleHTTPRequests,omitempty"` + DeepOperations *DeepOperations `json:"DeepOperations,omitempty"` +} + +// DeepOperations redfish structure +type DeepOperations struct { + DeepPATCH bool `json:"DeepPATCH,omitempty"` + DeepPOST bool `json:"DeepPOST,omitempty"` + MaxLevels int `json:"MaxLevels,omitempty"` } // ExpandQuery redfish structure diff --git a/lib-dmtf/model/storage.go b/lib-dmtf/model/storage.go index e49230615..425517b79 100644 --- a/lib-dmtf/model/storage.go +++ b/lib-dmtf/model/storage.go @@ -40,6 +40,22 @@ type Storage struct { DrivesCount int `json:"Drives@odata.count,omitempty"` RedundancyCount int `json:"Redundancy@odata.count,omitempty"` StorageControllersCount int `json:"StorageControllers@odata.count,omitempty"` + AutoVolumeCreate string `json:"AutoVolumeCreate,omitempty"` + Connections ConnectionCollection `json:"Connections,omitempty"` +} + +// ConnectionCollection redfish structure +type ConnectionCollection struct { + ODataContext string `json:"@odata.context,omitempty"` + ODataID string `json:"@odata.id,omitempty"` + ODataEtag string `json:"@odata.etag,omitempty"` + ODataType string `json:"@odata.type,omitempty"` + Description string `json:"Description,omitempty"` + Name string `json:"Name"` + OEM *Oem `json:"Oem,omitempty"` + Members []string `json:"Members"` + MembersODataCount int `json:"Members@odata.count"` + MembersODataNextLink string `json:"Members@odata.nextLink,omitempty"` } // StorageControllers redfish structure @@ -355,7 +371,7 @@ type Volume struct { ReplicaInfo *ReplicaInfo `json:"ReplicaInfo,omitempty"` ReplicaTargets []*Link `json:"ReplicaTargets,omitempty"` Status *StorageStatus `json:"Status,omitempty"` - StorageGroups *Link `json:"StorageGroups,omitempty"` + StorageGroups *Link `json:"StorageGroups,omitempty"` //deprecated StripSizeBytes int `json:"StripSizeBytes,omitempty"` VolumeType string `json:"VolumeType,omitempty"` VolumeUsage string `json:"VolumeUsage,omitempty"` diff --git a/lib-utilities/proto/events/events.proto b/lib-utilities/proto/events/events.proto index e55104de8..131a863d5 100644 --- a/lib-utilities/proto/events/events.proto +++ b/lib-utilities/proto/events/events.proto @@ -28,6 +28,7 @@ service Events { rpc IsAggregateHaveSubscription(EventUpdateRequest) returns (SubscribeEMBResponse){} rpc DeleteAggregateSubscriptionsRPC(EventUpdateRequest) returns (SubscribeEMBResponse){} rpc UpdateSubscriptionLocationRPC(UpdateSubscriptionLocation) returns (SubscribeEMBResponse) {} + rpc DeleteSubscription(EventRequest) returns (EventSubResponse) {} } message EventSubRequest { diff --git a/lib-utilities/services/eventsubscription.go b/lib-utilities/services/eventsubscription.go index dfcdad996..af5ee1347 100644 --- a/lib-utilities/services/eventsubscription.go +++ b/lib-utilities/services/eventsubscription.go @@ -43,10 +43,12 @@ func SubscribeToEMB(ctx context.Context, pluginID string, queueList []string) er } // DeleteSubscription calls the event service and delete all subscription realated to that server -func DeleteSubscription(ctx context.Context, uuid string) (*eventsproto.EventSubResponse, error) { +func DeleteSubscription(ctx context.Context, uuid string, + sessionToken string) (*eventsproto.EventSubResponse, error) { var resp eventsproto.EventSubResponse req := eventsproto.EventRequest{ - UUID: uuid, + UUID: uuid, + SessionToken: sessionToken, } conn, errConn := ODIMService.Client(Events) if errConn != nil { diff --git a/lib-utilities/services/pluginTask.go b/lib-utilities/services/pluginTask.go index 600edb59a..7a26e46e8 100644 --- a/lib-utilities/services/pluginTask.go +++ b/lib-utilities/services/pluginTask.go @@ -47,6 +47,43 @@ func SavePluginTaskInfo(ctx context.Context, pluginIP, pluginServerName, return nil } +// SaveEventSubscriptionID saves the event subscription id in db +// it receives taskID and subscription ID. taskID is the key. +func SaveEventSubscriptionID(ctx context.Context, taskID, subscriptionID string) error { + + table := "EventSubscriptionID" + connPool, err := redis.GetDBConnection(redis.InMemory) + if err != nil { + return errors.PackError(err.ErrNo(), "error while trying to connecting"+ + " to DB: ", err.Error()) + } + + if err = connPool.Create(table, taskID, subscriptionID); err != nil { + return errors.PackError(err.ErrNo(), "error while trying to insert"+ + " plugin task: ", err.Error()) + } + return nil +} + +// SaveEventSubscriptionID saves the event subscription id in db +// it receives taskID and subscription ID. taskID is the key. +func GetEventSubscriptionID(ctx context.Context, taskID string) (string, error) { + + table := "EventSubscriptionID" + connPool, err := redis.GetDBConnection(redis.InMemory) + if err != nil { + return "", errors.PackError(err.ErrNo(), "error while trying to connecting"+ + " to DB: ", err.Error()) + } + + value, err := connPool.Read(table, taskID) + if err != nil { + return "", errors.PackError(err.ErrNo(), "error while trying to insert"+ + " plugin task: ", err.Error()) + } + return value, nil +} + // createPluginTask will insert plugin task info in DB func createPluginTask(ctx context.Context, key string, value interface{}) *errors.Error { diff --git a/svc-aggregation/agmessagebus/publish_test.go b/svc-aggregation/agmessagebus/publish_test.go index 4b73bd749..e1cc1c7d8 100644 --- a/svc-aggregation/agmessagebus/publish_test.go +++ b/svc-aggregation/agmessagebus/publish_test.go @@ -134,7 +134,6 @@ func TestPublish(t *testing.T) { assert.NotEqual(t, nil, err, "Error should not be Nil for this scenario") } else { - fmt.Println(err) assert.Equal(t, nil, err, "Error should be Nil for this scenario") } diff --git a/svc-aggregation/go.mod b/svc-aggregation/go.mod index 3d6b178c6..7bc5afdab 100644 --- a/svc-aggregation/go.mod +++ b/svc-aggregation/go.mod @@ -18,7 +18,7 @@ require ( github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect github.com/CloudyKit/jet/v6 v6.2.0 // indirect github.com/Joker/jade v1.1.3 // indirect - github.com/ODIM-Project/ODIM/lib-persistence-manager v0.0.0-20201201072448-9772421f1b55 // indirect + github.com/ODIM-Project/ODIM/lib-persistence-manager v0.0.0-20230719110936-f43048b6407a // indirect github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/aymerick/douceur v0.2.0 // indirect diff --git a/svc-aggregation/rpc/aggregator.go b/svc-aggregation/rpc/aggregator.go index 891709ba8..db15c1c09 100644 --- a/svc-aggregation/rpc/aggregator.go +++ b/svc-aggregation/rpc/aggregator.go @@ -568,7 +568,7 @@ func (a *Aggregator) DeleteAggregationSource(ctx context.Context, req *aggregato var threadID int = 1 ctxt := context.WithValue(ctx, common.ThreadName, common.DeleteAggregationSource) ctxt = context.WithValue(ctxt, common.ThreadID, strconv.Itoa(threadID)) - go a.connector.DeleteAggregationSources(ctxt, taskID, targetURI, req) + go a.connector.DeleteAggregationSources(ctxt, taskID, targetURI, req, sessionUserName) threadID++ // return 202 Accepted var rpcResp = response.RPC{ diff --git a/svc-aggregation/rpc/common.go b/svc-aggregation/rpc/common.go index 0e9df61fa..05393876b 100644 --- a/svc-aggregation/rpc/common.go +++ b/svc-aggregation/rpc/common.go @@ -43,7 +43,7 @@ func GetAggregator() *Aggregator { CreateTask: services.CreateTask, CreateChildTask: services.CreateChildTask, UpdateTask: system.UpdateTaskData, - CreateSubcription: system.CreateDefaultEventSubscription, + CreateSubscription: system.CreateDefaultEventSubscription, PublishEvent: system.PublishEvent, GetPluginStatus: agcommon.GetPluginStatus, SubscribeToEMB: services.SubscribeToEMB, diff --git a/svc-aggregation/rpc/common_test.go b/svc-aggregation/rpc/common_test.go index 7eb34ed8c..00df3d589 100644 --- a/svc-aggregation/rpc/common_test.go +++ b/svc-aggregation/rpc/common_test.go @@ -42,7 +42,7 @@ var connector = &system.ExternalInterface{ UpdateTask: mockUpdateTask, DecryptPassword: stubDevicePassword, GetPluginStatus: GetPluginStatusForTesting, - CreateSubcription: EventFunctionsForTesting, + CreateSubscription: EventFunctionsForTesting, PublishEvent: PostEventFunctionForTesting, EncryptPassword: stubDevicePassword, DeleteComputeSystem: deleteComputeforTest, @@ -119,7 +119,7 @@ func deleteSystemforTest(key string) *errors.Error { return nil } -func mockDeleteSubscription(ctx context.Context, uuid string) (*eventsproto.EventSubResponse, error) { +func mockDeleteSubscription(ctx context.Context, uuid string, sessionName string) (*eventsproto.EventSubResponse, error) { if uuid == "/redfish/v1/systems/delete-subscription-error.1" { return nil, fmt.Errorf("error while trying to delete event subcription") } else if uuid == "/redfish/v1/systems/unexpected-statuscode.1" { diff --git a/svc-aggregation/system/addaggregationsource_test.go b/svc-aggregation/system/addaggregationsource_test.go index 4309e3c47..7dc11d4d8 100644 --- a/svc-aggregation/system/addaggregationsource_test.go +++ b/svc-aggregation/system/addaggregationsource_test.go @@ -938,7 +938,7 @@ func getMockExternalInterface() *ExternalInterface { Auth: mockIsAuthorized, CreateChildTask: mockCreateChildTask, UpdateTask: mockUpdateTask, - CreateSubcription: EventFunctionsForTesting, + CreateSubscription: EventFunctionsForTesting, PublishEvent: PostEventFunctionForTesting, GetPluginStatus: GetPluginStatusForTesting, PublishEventMB: mockPublishEventMB, diff --git a/svc-aggregation/system/addcompute.go b/svc-aggregation/system/addcompute.go index 5d2e25e08..0184436ff 100644 --- a/svc-aggregation/system/addcompute.go +++ b/svc-aggregation/system/addcompute.go @@ -136,7 +136,7 @@ func (e *ExternalInterface) addCompute(ctx context.Context, taskID, targetURI, p pluginContactRequest.OID = "/redfish/v1/Systems" pluginContactRequest.DeviceUUID = saveSystem.DeviceUUID pluginContactRequest.HTTPMethodType = http.MethodGet - pluginContactRequest.CreateSubcription = e.CreateSubcription + pluginContactRequest.CreateSubscription = e.CreateSubscription pluginContactRequest.PublishEvent = e.PublishEvent pluginContactRequest.BMCAddress = saveSystem.ManagerAddress @@ -315,7 +315,7 @@ func (e *ExternalInterface) addCompute(ctx context.Context, taskID, targetURI, p urlList := h.SystemURL urlList = append(urlList, chassisList...) urlList = append(urlList, managersList...) - pluginContactRequest.CreateSubcription(ctx, urlList) + pluginContactRequest.CreateSubscription(ctx, urlList) pluginContactRequest.PublishEvent(ctx, h.SystemURL, "SystemsCollection") diff --git a/svc-aggregation/system/common.go b/svc-aggregation/system/common.go index 248627696..53bd0c732 100644 --- a/svc-aggregation/system/common.go +++ b/svc-aggregation/system/common.go @@ -81,7 +81,7 @@ type ExternalInterface struct { CreateChildTask func(context.Context, string, string) (string, error) CreateTask func(context.Context, string) (string, error) UpdateTask func(context.Context, common.TaskData) error - CreateSubcription func(context.Context, []string) + CreateSubscription func(context.Context, []string) PublishEvent func(context.Context, []string, string) PublishEventMB func(context.Context, string, string, string) GetPluginStatus func(context.Context, agmodel.Plugin) bool @@ -90,7 +90,7 @@ type ExternalInterface struct { DecryptPassword func([]byte) ([]byte, error) DeleteComputeSystem func(int, string) *errors.Error DeleteSystem func(string) *errors.Error - DeleteEventSubscription func(context.Context, string) (*eventsproto.EventSubResponse, error) + DeleteEventSubscription func(context.Context, string, string) (*eventsproto.EventSubResponse, error) EventNotification func(context.Context, string, string, string, agmessagebus.MQBusCommunicator) error GetAllKeysFromTable func(context.Context, string) ([]string, error) GetConnectionMethod func(context.Context, string) (agmodel.ConnectionMethod, *errors.Error) @@ -114,29 +114,29 @@ type responseStatus struct { } type getResourceRequest struct { - Data []byte - Username string - Password string - SystemID string - DeviceUUID string - DeviceInfo interface{} - LoginCredentials map[string]string - ParentOID string - OID string - ContactClient func(context.Context, string, string, string, string, interface{}, map[string]string) (*http.Response, error) - OemFlag bool - Plugin agmodel.Plugin - TaskRequest string - HTTPMethodType string - Token string - StatusPoll bool - CreateSubcription func(context.Context, []string) - PublishEvent func(context.Context, []string, string) - GetPluginStatus func(context.Context, agmodel.Plugin) bool - UpdateFlag bool - TargetURI string - UpdateTask func(context.Context, common.TaskData) error - BMCAddress string + Data []byte + Username string + Password string + SystemID string + DeviceUUID string + DeviceInfo interface{} + LoginCredentials map[string]string + ParentOID string + OID string + ContactClient func(context.Context, string, string, string, string, interface{}, map[string]string) (*http.Response, error) + OemFlag bool + Plugin agmodel.Plugin + TaskRequest string + HTTPMethodType string + Token string + StatusPoll bool + CreateSubscription func(context.Context, []string) + PublishEvent func(context.Context, []string, string) + GetPluginStatus func(context.Context, agmodel.Plugin) bool + UpdateFlag bool + TargetURI string + UpdateTask func(context.Context, common.TaskData) error + BMCAddress string } type respHolder struct { diff --git a/svc-aggregation/system/deleteaggregationsource.go b/svc-aggregation/system/deleteaggregationsource.go index 84ead5aa4..b70e435db 100644 --- a/svc-aggregation/system/deleteaggregationsource.go +++ b/svc-aggregation/system/deleteaggregationsource.go @@ -34,7 +34,8 @@ import ( ) // DeleteAggregationSources is used to delete aggregation sources -func (e *ExternalInterface) DeleteAggregationSources(ctx context.Context, taskID string, targetURI string, req *aggregatorproto.AggregatorRequest) error { +func (e *ExternalInterface) DeleteAggregationSources(ctx context.Context, taskID string, targetURI string, + req *aggregatorproto.AggregatorRequest, sessionUserName string) error { var task = common.TaskData{ TaskID: taskID, TargetURI: targetURI, @@ -58,7 +59,7 @@ func (e *ExternalInterface) DeleteAggregationSources(ctx context.Context, taskID go runtime.Goexit() } l.LogWithFields(ctx).Debugf("request data for delete aggregation source: %s", string(req.RequestBody)) - data := e.DeleteAggregationSource(ctx, req) + data := e.DeleteAggregationSource(ctx, req, sessionUserName) err = e.UpdateTask(ctx, common.TaskData{ TaskID: taskID, TargetURI: targetURI, @@ -83,7 +84,8 @@ func (e *ExternalInterface) DeleteAggregationSources(ctx context.Context, taskID } // DeleteAggregationSource is the handler for removing bmc or manager -func (e *ExternalInterface) DeleteAggregationSource(ctx context.Context, req *aggregatorproto.AggregatorRequest) response.RPC { +func (e *ExternalInterface) DeleteAggregationSource(ctx context.Context, + req *aggregatorproto.AggregatorRequest, sessionUserName string) response.RPC { var resp response.RPC aggregationSource, dbErr := agmodel.GetAggregationSourceInfo(ctx, req.URL) @@ -145,7 +147,7 @@ func (e *ExternalInterface) DeleteAggregationSource(ctx context.Context, req *ag } for _, systemURI := range systemList { index := strings.LastIndexAny(systemURI, "/") - resp = e.deleteCompute(ctx, systemURI, index, target.PluginID) + resp = e.deleteCompute(ctx, systemURI, index, target.PluginID, sessionUserName) } removeAggregationSourceFromAggregates(ctx, systemList) } @@ -385,7 +387,8 @@ func (e *ExternalInterface) deletePlugin(ctx context.Context, oid string) respon return resp } -func (e *ExternalInterface) deleteCompute(ctx context.Context, key string, index int, pluginID string) response.RPC { +func (e *ExternalInterface) deleteCompute(ctx context.Context, key string, index int, pluginID string, + sessionUserName string) response.RPC { var resp response.RPC // check whether the any system operation is under progress systemOperation, dbErr := agmodel.GetSystemOperationInfo(ctx, strings.TrimSuffix(key, "/")) @@ -441,7 +444,7 @@ func (e *ExternalInterface) deleteCompute(ctx context.Context, key string, index } }() // Delete Subscription on odimra and also on device - subResponse, err := e.DeleteEventSubscription(ctx, key) + subResponse, err := e.DeleteEventSubscription(ctx, key, sessionUserName) if err != nil && subResponse == nil { errMsg := fmt.Sprintf("error while trying to delete subscriptions: %v", err) l.LogWithFields(ctx).Error(errMsg) diff --git a/svc-aggregation/system/deleteaggregationsource_test.go b/svc-aggregation/system/deleteaggregationsource_test.go index 178209106..7c8875450 100644 --- a/svc-aggregation/system/deleteaggregationsource_test.go +++ b/svc-aggregation/system/deleteaggregationsource_test.go @@ -66,7 +66,7 @@ func deleteSystemforTest(key string) *errors.Error { return nil } -func mockDeleteSubscription(ctx context.Context, uuid string) (*eventsproto.EventSubResponse, error) { +func mockDeleteSubscription(ctx context.Context, uuid, sessionId string) (*eventsproto.EventSubResponse, error) { if uuid == "/redfish/v1/Systems/ef83e569-7336-492a-aaee-31c02d9db832.1" { return nil, fmt.Errorf("error while trying to delete event subcription") } else if uuid == "/redfish/v1/Systems/unexpected-statuscode.1" { @@ -153,7 +153,8 @@ func mockLogServicesCollectionData(id string, data map[string]interface{}) error func TestDeleteAggregationSourceWithRediscovery(t *testing.T) { d := getMockExternalInterface() type args struct { - req *aggregatorproto.AggregatorRequest + req *aggregatorproto.AggregatorRequest + sessionName string } config.SetUpMockConfig(t) defer func() { @@ -227,13 +228,14 @@ func TestDeleteAggregationSourceWithRediscovery(t *testing.T) { SessionToken: "SessionToken", URL: "/redfish/v1/AggregationService/AggregationSources/ef83e569-7336-492a-aaee-31c02d9db831", }, + sessionName: "dummy", }, want: http.StatusNotAcceptable, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := d.DeleteAggregationSource(ctx, tt.args.req) + got := d.DeleteAggregationSource(ctx, tt.args.req, tt.args.sessionName) if got.StatusCode != tt.want { t.Errorf("DeleteAggregationSource() = %v, want %v", got, tt.want) } @@ -338,7 +340,8 @@ func TestExternalInterface_DeleteAggregationSourceManager(t *testing.T) { t.Fatalf("error: %v", err) } type args struct { - req *aggregatorproto.AggregatorRequest + req *aggregatorproto.AggregatorRequest + sessionName string } tests := []struct { name string @@ -352,6 +355,7 @@ func TestExternalInterface_DeleteAggregationSourceManager(t *testing.T) { SessionToken: "SessionToken", URL: "/redfish/v1/AggregationService/AggregationSources/123456", }, + sessionName: "dummy", }, want: http.StatusNoContent, }, @@ -362,6 +366,7 @@ func TestExternalInterface_DeleteAggregationSourceManager(t *testing.T) { SessionToken: "SessionToken", URL: "/redfish/v1/AggregationService/AggregationSources/123455", }, + sessionName: "dummy", }, want: http.StatusNotAcceptable, }, @@ -372,6 +377,7 @@ func TestExternalInterface_DeleteAggregationSourceManager(t *testing.T) { SessionToken: "SessionToken", URL: "/redfish/v1/AggregationService/AggregationSources/123434", }, + sessionName: "dummy", }, want: http.StatusNotFound, }, @@ -382,13 +388,14 @@ func TestExternalInterface_DeleteAggregationSourceManager(t *testing.T) { SessionToken: "SessionToken", URL: "/redfish/v1/AggregationService/AggregationSources/123457", }, + sessionName: "dummy", }, want: http.StatusNotAcceptable, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := d.DeleteAggregationSource(ctx, tt.args.req) + got := d.DeleteAggregationSource(ctx, tt.args.req, tt.args.sessionName) if got.StatusCode != tt.want { t.Errorf("DeleteAggregationSource() = %v, want %v", got, tt.want) } @@ -478,7 +485,8 @@ func TestExternalInterface_DeleteBMC(t *testing.T) { t.Fatalf("error: %v", err) } type args struct { - req *aggregatorproto.AggregatorRequest + req *aggregatorproto.AggregatorRequest + sessionName string } tests := []struct { name string @@ -492,6 +500,7 @@ func TestExternalInterface_DeleteBMC(t *testing.T) { SessionToken: "SessionToken", URL: "/redfish/v1/AggregationService/AggregationSources/ef83e569-7336-492a-aaee-31c02d9db831", }, + sessionName: "dummy", }, want: http.StatusNoContent, }, @@ -502,13 +511,14 @@ func TestExternalInterface_DeleteBMC(t *testing.T) { SessionToken: "SessionToken", URL: "/redfish/v1/AggregationService/AggregationSources/ef83e569-7336-492a-aaee-31c02d9db832", }, + sessionName: "dummy", }, want: http.StatusInternalServerError, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := d.DeleteAggregationSource(ctx, tt.args.req) + got := d.DeleteAggregationSource(ctx, tt.args.req, tt.args.sessionName) if got.StatusCode != tt.want { t.Errorf("DeleteAggregationSource() = %v, want %v", got, tt.want) } diff --git a/svc-api/rpc/fakeStruct_testfile.go b/svc-api/rpc/fakeStruct_testfile.go index cf8a6ae65..ad997b68d 100644 --- a/svc-api/rpc/fakeStruct_testfile.go +++ b/svc-api/rpc/fakeStruct_testfile.go @@ -240,6 +240,9 @@ func (fakeStruct) SubscribeEMB(ctx context.Context, in *eventsproto.SubscribeEMB func (fakeStruct) UpdateSubscriptionLocationRPC(ctx context.Context, in *eventsproto.UpdateSubscriptionLocation, opts ...grpc.CallOption) (*eventsproto.SubscribeEMBResponse, error) { return nil, errors.New("fakeError") } +func (fakeStruct) DeleteSubscription(ctx context.Context, in *eventsproto.EventRequest, opts ...grpc.CallOption) (*eventsproto.EventSubResponse, error) { + return nil, errors.New("fakeError") +} //--------------------------------------FABRICS-------------------------------------- diff --git a/svc-events/events/deletesubscription.go b/svc-events/events/deletesubscription.go index db31a1aea..ac53bb25f 100644 --- a/svc-events/events/deletesubscription.go +++ b/svc-events/events/deletesubscription.go @@ -50,14 +50,20 @@ var ( ) // DeleteEventSubscriptions delete subscription data against given URL -func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req *eventsproto.EventRequest) response.RPC { +func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req *eventsproto.EventRequest, taskId string) response.RPC { var resp response.RPC originResource := req.UUID uuid, err := getUUID(originResource) + var ( + percentComplete int32 = 100 + targetURI = "/redfish/v1/EventService/Subscriptions" + ) if err != nil { msgArgs := []interface{}{"OriginResource", originResource} evcommon.GenErrorResponse(err.Error(), response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) l.LogWithFields(ctx).Error(err.Error()) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } target, err := e.GetTarget(uuid) @@ -66,6 +72,8 @@ func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req * errorMessage := err.Error() msgArgs := []interface{}{"uuid", uuid} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } deviceIPAddress, err := GetIPFromHostNameFunc(target.ManagerAddress) @@ -73,6 +81,8 @@ func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req * msgArgs := []interface{}{"Host", target.ManagerAddress} evcommon.GenErrorResponse(err.Error(), response.ResourceNotFound, http.StatusNotFound, msgArgs, &resp) l.LogWithFields(ctx).Error(err.Error()) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } searchKey := evcommon.GetSearchKey(deviceIPAddress, evmodel.SubscriptionIndex) @@ -82,6 +92,8 @@ func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req * errorMessage := err.Error() msgArgs := []interface{}{"Host", target.ManagerAddress} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusNotFound, msgArgs, &resp) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } l.LogWithFields(ctx).Debug("Number of subscription present :", strconv.Itoa(len(subscriptionDetails))) @@ -92,17 +104,20 @@ func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req * msgArgs := []interface{}{""} evcommon.GenErrorResponse(errorMessage, response.InternalError, http.StatusInternalServerError, msgArgs, &resp) l.LogWithFields(ctx).Error(errorMessage) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } target.Password = decryptedPasswordByte // Delete Event Subscription from device also err = e.deleteSubscription(ctx, target, originResource) - if err != nil { l.LogWithFields(ctx).Error("error while deleting event subscription details : " + err.Error()) msgArgs := []interface{}{"Host", target.ManagerAddress} evcommon.GenErrorResponse(err.Error(), response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } searchKey = evcommon.GetSearchKey(deviceIPAddress, evmodel.DeviceSubscriptionIndex) @@ -112,6 +127,8 @@ func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req * msgArgs := []interface{}{"Host", target.ManagerAddress} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) l.LogWithFields(ctx).Error(errorMessage) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } originResource = deviceSubscription.OriginResources[0] @@ -130,6 +147,8 @@ func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req * msgArgs := []interface{}{"SubscriptionID", evtSubscription.SubscriptionID} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) l.LogWithFields(ctx).Error(errorMessage) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } } else { @@ -142,6 +161,8 @@ func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req * msgArgs := []interface{}{"SubscriptionID", evtSubscription.SubscriptionID} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) l.LogWithFields(ctx).Error(errorMessage) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } } @@ -152,6 +173,8 @@ func (e *ExternalInterfaces) DeleteEventSubscriptions(ctx context.Context, req * errorMessage := "Error while deleting device subscription : " + err.Error() l.LogWithFields(ctx).Error(errorMessage) } + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.OK, common.Completed, + percentComplete, http.MethodDelete)) resp.StatusCode = http.StatusNoContent resp.StatusMessage = response.ResourceRemoved @@ -173,23 +196,35 @@ func (e *ExternalInterfaces) deleteSubscription(ctx context.Context, target *com } // DeleteEventSubscriptionsDetails delete subscription data against given subscription id -func (e *ExternalInterfaces) DeleteEventSubscriptionsDetails(ctx context.Context, req *eventsproto.EventRequest) response.RPC { +func (e *ExternalInterfaces) DeleteEventSubscriptionsDetails(ctx context.Context, req *eventsproto.EventRequest, + sessionUserName string, taskId string) response.RPC { var resp response.RPC - authResp, err := e.Auth(ctx, req.SessionToken, []string{common.PrivilegeConfigureComponents}, []string{}) + var ( + percentComplete int32 = 100 + targetURI = "/redfish/v1/EventService/Subscriptions" + ) + reqCtx := common.CreateNewRequestContext(ctx) + reqCtx = common.CreateMetadata(reqCtx) + authResp, err := e.Auth(reqCtx, req.SessionToken, []string{common.PrivilegeConfigureComponents}, []string{}) if authResp.StatusCode != http.StatusOK { errMsg := fmt.Sprintf("error while trying to authenticate session: status code: %v, status message: %v", authResp.StatusCode, authResp.StatusMessage) if err != nil { errMsg = errMsg + ": " + err.Error() } l.LogWithFields(ctx).Error(errMsg) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return authResp } subscriptionDetails, err := e.GetEvtSubscriptions(req.EventSubscriptionID) if err != nil && !strings.Contains(err.Error(), "No data found for the key") { - l.LogWithFields(ctx).Error("error while deleting eventsubscription details : " + err.Error()) + l.LogWithFields(ctx).Error("error while deleting event subscription details : " + err.Error()) errorMessage := err.Error() msgArgs := []interface{}{"SubscriptionID", req.EventSubscriptionID} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) + return resp } if len(subscriptionDetails) < 1 { @@ -197,6 +232,9 @@ func (e *ExternalInterfaces) DeleteEventSubscriptionsDetails(ctx context.Context l.LogWithFields(ctx).Error(errorMessage) var msgArgs = []interface{}{"SubscriptionID", req.EventSubscriptionID} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusNotFound, msgArgs, &resp) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) + return resp } for _, evtSubscription := range subscriptionDetails { @@ -207,15 +245,26 @@ func (e *ExternalInterfaces) DeleteEventSubscriptionsDetails(ctx context.Context l.LogWithFields(ctx).Error(errorMessage) var msgArgs = []interface{}{"SubscriptionID", req.EventSubscriptionID} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusNotFound, msgArgs, &resp) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) + return resp } // Delete and re subscribe Event Subscription - err = e.deleteAndReSubscribeToEvents(ctx, evtSubscription, req.SessionToken) + isStatusAccepted, err := e.deleteAndReSubscribeToEvents(ctx, evtSubscription, req.SessionToken, sessionUserName, taskId) if err != nil { errorMessage := err.Error() msgArgs := []interface{}{"SubscriptionID", req.EventSubscriptionID} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) + + return resp + } + if isStatusAccepted { + services.SaveEventSubscriptionID(ctx, taskId, evtSubscription.SubscriptionID) + resp.StatusCode = http.StatusAccepted return resp } @@ -226,6 +275,8 @@ func (e *ExternalInterfaces) DeleteEventSubscriptionsDetails(ctx context.Context errorMessage := err.Error() msgArgs := []interface{}{"SubscriptionID", req.EventSubscriptionID} evcommon.GenErrorResponse(errorMessage, response.ResourceNotFound, http.StatusBadRequest, msgArgs, &resp) + e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Exception, + common.Critical, percentComplete, http.MethodDelete)) return resp } } @@ -242,11 +293,17 @@ func (e *ExternalInterfaces) DeleteEventSubscriptionsDetails(ctx context.Context commonResponse.CreateGenericResponse(resp.StatusMessage) resp.Body = commonResponse + err = e.UpdateTask(ctx, fillTaskData(taskId, targetURI, string(req.EventSubscriptionID), resp, common.Completed, + common.OK, percentComplete, http.MethodDelete)) + return resp } // This function is to delete and re subscribe for Event Subscriptions -func (e *ExternalInterfaces) deleteAndReSubscribeToEvents(ctx context.Context, evtSubscription evmodel.SubscriptionResource, sessionToken string) error { +func (e *ExternalInterfaces) deleteAndReSubscribeToEvents(ctx context.Context, evtSubscription evmodel.SubscriptionResource, + sessionToken, sessionUserName string, taskID string) (bool, error) { + + var isStatusAccepted bool originResources := evtSubscription.EventDestination.OriginResources for _, origin := range originResources { // ignore if origin is empty @@ -255,7 +312,7 @@ func (e *ExternalInterfaces) deleteAndReSubscribeToEvents(ctx context.Context, e } subscriptionDetails, err := e.GetEvtSubscriptions(origin.Oid) if err != nil { - return err + return isStatusAccepted, err } subscriptionDetails = append([]evmodel.SubscriptionResource{{ EventDestination: &model.EventDestination{ @@ -273,7 +330,7 @@ func (e *ExternalInterfaces) deleteAndReSubscribeToEvents(ctx context.Context, e var deleteFlag bool if len(subscriptionDetails) < 1 { - return fmt.Errorf("subscription details not found for subscription id: %s", origin) + return isStatusAccepted, fmt.Errorf("subscription details not found for subscription id: %s", origin) } else if len(subscriptionDetails) == 1 { deleteFlag = true } @@ -320,13 +377,12 @@ func (e *ExternalInterfaces) deleteAndReSubscribeToEvents(ctx context.Context, e Protocol: protocol, Destination: destination, } - - err = e.subscribe(ctx, subscriptionPost, origin.Oid, deleteFlag, sessionToken) + isStatusAccepted, err = e.subscribe(ctx, subscriptionPost, origin.Oid, deleteFlag, sessionToken, sessionUserName, taskID, evtSubscription.SubscriptionID) if err != nil { - return err + return isStatusAccepted, err } } - return nil + return isStatusAccepted, nil } func isCollectionOriginResourceURI(origin string) bool { @@ -358,40 +414,52 @@ func isCollectionOriginResourceURI(origin string) bool { } // Subscribe to the Event Subscription -func (e *ExternalInterfaces) subscribe(ctx context.Context, subscriptionPost model.EventDestination, origin string, deleteflag bool, sessionToken string) error { +func (e *ExternalInterfaces) subscribe(ctx context.Context, subscriptionPost model.EventDestination, origin string, + deleteFlag bool, sessionToken string, sessionUserName string, taskId, subscriptionId string) (bool, error) { + var isStatusAccepted bool if strings.Contains(origin, "Fabrics") { - return e.resubscribeFabricsSubscription(ctx, subscriptionPost, origin, deleteflag) + return isStatusAccepted, e.resubscribeFabricsSubscription(ctx, subscriptionPost, origin, sessionUserName, taskId, deleteFlag) } if strings.Contains(origin, "/redfish/v1/AggregationService/Aggregates") { - return e.resubscribeAggregateSubscription(ctx, subscriptionPost, origin, deleteflag, sessionToken) + return isStatusAccepted, e.resubscribeAggregateSubscription(ctx, subscriptionPost, origin, deleteFlag, sessionToken, sessionUserName, taskId, subscriptionId) } originResource := origin if isCollectionOriginResourceURI(originResource) { + isStatusAccepted = true l.LogWithFields(ctx).Error("Collection of origin resource:" + originResource) - return nil + subtaskID := e.CreateSubTask(ctx, sessionUserName, taskId) + resp := response.RPC{} + resp.StatusCode = 200 + resp.Body = subscriptionPost + services.SaveEventSubscriptionID(ctx, taskId, subscriptionId) + + e.UpdateTask(ctx, fillTaskData(subtaskID, originResource, "", resp, common.Completed, + common.OK, 100, http.MethodDelete)) + return isStatusAccepted, nil } target, _, err := e.getTargetDetails(originResource) if err != nil { - return err + return isStatusAccepted, err } plugin, errs := e.GetPluginData(target.PluginID) if errs != nil { - return errs + return isStatusAccepted, errs } postBody, err := json.Marshal(subscriptionPost) if err != nil { - return fmt.Errorf("error while marshalling subscription details: %s", err) + return isStatusAccepted, fmt.Errorf("error while marshalling subscription details: %s", err) } target.PostBody = postBody - _, err = e.DeleteSubscriptions(ctx, origin, "", plugin, target) + err = e.GetSubscriptionLocation(ctx, target) if err != nil { - return err + return isStatusAccepted, err } - // if deleteflag is true then only one document is there + + // if deleteFlag is true then only one document is there // so don't re subscribe again - if deleteflag { - return nil + if deleteFlag { + return isStatusAccepted, nil } var contactRequest evcommon.PluginContactRequest @@ -399,7 +467,7 @@ func (e *ExternalInterfaces) subscribe(ctx context.Context, subscriptionPost mod if strings.EqualFold(plugin.PreferredAuthType, "XAuthToken") { token := e.getPluginToken(ctx, plugin) if token == "" { - return fmt.Errorf("error: Unable to create session with plugin " + plugin.ID) + return isStatusAccepted, fmt.Errorf("error: Unable to create session with plugin " + plugin.ID) } contactRequest.Token = token } else { @@ -409,14 +477,30 @@ func (e *ExternalInterfaces) subscribe(ctx context.Context, subscriptionPost mod } } + + // Note: commenting delete subscription API call and calling patch API instead + // a subtask is created for each patch call + + subtaskID := e.CreateSubTask(ctx, sessionUserName, taskId) contactRequest.URL = "/ODIM/v1/Subscriptions" - contactRequest.HTTPMethodType = http.MethodPost + contactRequest.HTTPMethodType = http.MethodPatch contactRequest.PostBody = target - - _, loc, _, _, err := e.PluginCall(ctx, contactRequest) + createResponse, loc, _, pluginIP, err := e.PluginCall(ctx, contactRequest) if err != nil { - return err + e.UpdateTask(ctx, fillTaskData(subtaskID, contactRequest.URL, "", createResponse, common.Exception, + common.Critical, 100, http.MethodPatch)) + return isStatusAccepted, err } + if createResponse.StatusCode == http.StatusAccepted { + isStatusAccepted = true + services.SavePluginTaskInfo(ctx, pluginIP, plugin.IP, + subtaskID, loc) + return isStatusAccepted, nil + } + + e.UpdateTask(ctx, fillTaskData(subtaskID, contactRequest.URL, "", createResponse, common.Completed, + common.OK, 100, http.MethodPatch)) + // Update Location to all destination of device if already subscribed to the device var resp response.RPC deviceIPAddress, err := common.GetIPFromHostName(target.ManagerAddress) @@ -428,14 +512,14 @@ func (e *ExternalInterfaces) subscribe(ctx context.Context, subscriptionPost mod searchKey := evcommon.GetSearchKey(deviceIPAddress, evmodel.DeviceSubscriptionIndex) devSub, err := e.GetDeviceSubscriptions(searchKey) if err != nil { - return err + return isStatusAccepted, err } deviceSub := common.DeviceSubscription{ EventHostIP: devSub.EventHostIP, Location: loc, OriginResources: devSub.OriginResources, } - return e.UpdateDeviceSubscriptionLocation(deviceSub) + return isStatusAccepted, e.UpdateDeviceSubscriptionLocation(deviceSub) } @@ -509,7 +593,8 @@ func (e *ExternalInterfaces) DeleteFabricsSubscription(ctx context.Context, orig } // resubscribeFabricsSubscription updates subscription fabric subscription details by forming the super set of MessageIDs,EventTypes and ResourceTypes -func (e *ExternalInterfaces) resubscribeFabricsSubscription(ctx context.Context, subscriptionPost model.EventDestination, origin string, deleteflag bool) error { +func (e *ExternalInterfaces) resubscribeFabricsSubscription(ctx context.Context, subscriptionPost model.EventDestination, + sessionUserName string, taskId string, origin string, deleteFlag bool) error { originResources := e.getSubordinateResourcesFromCollection(origin) for _, origin := range originResources { originResource := origin @@ -538,9 +623,9 @@ func (e *ExternalInterfaces) resubscribeFabricsSubscription(ctx context.Context, } return err } - // if deleteflag is true then only one document is there + // if deleteFlag is true then only one document is there // so don't re subscribe again - if deleteflag { + if deleteFlag { return nil } var contactRequest evcommon.PluginContactRequest @@ -572,6 +657,7 @@ func (e *ExternalInterfaces) resubscribeFabricsSubscription(ctx context.Context, reqData = strings.Replace(string(postBody), key, value, -1) } + subtaskID := e.CreateSubTask(ctx, sessionUserName, taskId) // recreating the subscription contactRequest.URL = "/ODIM/v1/Subscriptions" contactRequest.HTTPMethodType = http.MethodPost @@ -580,16 +666,31 @@ func (e *ExternalInterfaces) resubscribeFabricsSubscription(ctx context.Context, return err } l.LogWithFields(ctx).Info("Resubscribe request" + reqData) - response, loc, _, _, err := e.PluginCall(ctx, contactRequest) + response, loc, _, pluginIP, err := e.PluginCall(ctx, contactRequest) if err != nil { + e.UpdateTask(ctx, fillTaskData(subtaskID, contactRequest.URL, "", response, common.Exception, + common.Critical, 100, http.MethodPost)) return err } + + if response.StatusCode == http.StatusAccepted { + services.SavePluginTaskInfo(ctx, pluginIP, plugin.IP, + subtaskID, loc) + return nil + } + if response.StatusCode == http.StatusUnauthorized && strings.EqualFold(plugin.PreferredAuthType, "XAuthToken") { _, _, _, _, err = e.retryEventOperation(ctx, contactRequest) if err != nil { + e.UpdateTask(ctx, fillTaskData(subtaskID, contactRequest.URL, "", response, common.Exception, + common.Critical, 100, http.MethodPost)) return err } } + + e.UpdateTask(ctx, fillTaskData(subtaskID, contactRequest.URL, "", response, common.ComputerSystemReset, + common.OK, 100, http.MethodPost)) + addr, err := common.GetIPFromHostName(plugin.IP) if err != nil { return fmt.Errorf(err.Error()) @@ -724,14 +825,15 @@ func getAggregateSystemList(origin string, sessionToken string) ([]model.Link, e // resubscribeAggregateSubscription method subscribe event for // aggregate system members -func (e *ExternalInterfaces) resubscribeAggregateSubscription(ctx context.Context, subscriptionPost model.EventDestination, origin string, deleteflag bool, sessionToken string) error { +func (e *ExternalInterfaces) resubscribeAggregateSubscription(ctx context.Context, subscriptionPost model.EventDestination, + origin string, deleteFlag bool, sessionToken string, sessionUserName string, taskId, subscriptionId string) error { originResource := origin systems, err := getAggregateSystemList(originResource, sessionToken) if err != nil { return nil } for _, system := range systems { - err = e.subscribe(ctx, subscriptionPost, system.Oid, deleteflag, sessionToken) + _, err = e.subscribe(ctx, subscriptionPost, system.Oid, deleteFlag, sessionToken, sessionUserName, taskId, subscriptionId) if err != nil { return err } diff --git a/svc-events/events/deletesubscription_test.go b/svc-events/events/deletesubscription_test.go index 6d79ec3d9..c2230a2fb 100644 --- a/svc-events/events/deletesubscription_test.go +++ b/svc-events/events/deletesubscription_test.go @@ -50,18 +50,18 @@ func TestDeleteEventSubscription(t *testing.T) { SessionToken: "validToken", EventSubscriptionID: "81de0110-c35a-4859-984c-072d6c5a32d7", } - resp := pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req) + resp := pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req, "admin", "1225122") data := resp.Body.(response.Response) assert.Equal(t, http.StatusOK, int(resp.StatusCode), "Status Code should be StatusOK") assert.Equal(t, "81de0110-c35a-4859-984c-072d6c5a32d7", data.ID, "ID should be 81de0110-c35a-4859-984c-072d6c5a32d7") pc.DB.GetEvtSubscriptions = func(s string) ([]evmodel.SubscriptionResource, error) { return nil, &errors.Error{} } - resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req, "admin", "1225122") assert.Equal(t, http.StatusBadRequest, int(resp.StatusCode), "Status Code should be StatusOK") pc = getMockMethods() pc.DB.DeleteEvtSubscription = func(s string) error { return &errors.Error{} } - resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req, "admin", "1225122") assert.Equal(t, http.StatusBadRequest, int(resp.StatusCode), "Status Code should be StatusOK") pc = getMockMethods() // positive test case with basic auth type @@ -69,7 +69,7 @@ func TestDeleteEventSubscription(t *testing.T) { SessionToken: "validToken", EventSubscriptionID: "71de0110-c35a-4859-984c-072d6c5a32d8", } - resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req, "admin", "1225122") assert.Equal(t, http.StatusOK, int(resp.StatusCode), "Status Code should be StatusOK") // positive test case deletion of collection subscription @@ -77,8 +77,8 @@ func TestDeleteEventSubscription(t *testing.T) { SessionToken: "validToken", EventSubscriptionID: "71de0110-c35a-4859-984c-072d6c5a3211", } - resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req) - assert.Equal(t, http.StatusOK, int(resp.StatusCode), "Status Code should be StatusOK") + resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req, "admin", "1225122") + assert.Equal(t, http.StatusAccepted, int(resp.StatusCode), "Status Code should be StatusOK") // Negative test cases // if subscription id is bot present @@ -86,14 +86,14 @@ func TestDeleteEventSubscription(t *testing.T) { SessionToken: "validToken", EventSubscriptionID: "de018110-4859-984c-c35a-0a32d772d6c5", } - resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req1) + resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req1, "admin", "1225122") assert.Equal(t, http.StatusNotFound, int(resp.StatusCode), "Status Code should be StatusNotFound") // Invalid token req2 := &eventsproto.EventRequest{ SessionToken: "InValidToken", } - resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req2) + resp = pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req2, "admin", "1225122") assert.Equal(t, http.StatusUnauthorized, int(resp.StatusCode), "Status Code should be StatusUnauthorized") } @@ -110,7 +110,7 @@ func TestDeleteEventSubscriptionOnDeletedServer(t *testing.T) { SessionToken: "validToken", UUID: "/redfish/v1/Systems/6d4a0a66-7efa-578e-83cf-44dc68d2874e.1", } - resp := pc.DeleteEventSubscriptions(evcommon.MockContext(), req) + resp := pc.DeleteEventSubscriptions(evcommon.MockContext(), req, "1225122") assert.Equal(t, http.StatusNoContent, int(resp.StatusCode), "Status Code should be StatusNoContent") // Negative test cases @@ -119,7 +119,7 @@ func TestDeleteEventSubscriptionOnDeletedServer(t *testing.T) { SessionToken: "validToken", UUID: "de018110-4859-984c-c35a-0a32d772d6c5", } - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req1) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req1, "1225122") assert.Equal(t, http.StatusBadRequest, int(resp.StatusCode), "Status Code should be StatusNotFound") // if UUID is is not present in DB @@ -128,20 +128,20 @@ func TestDeleteEventSubscriptionOnDeletedServer(t *testing.T) { UUID: "/redfish/v1/Systems/de018110-4859-984c-c35a-0a32d772d6c5.1", } - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req1) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req1, "1225122") assert.Equal(t, http.StatusBadRequest, int(resp.StatusCode), "Status Code should be StatusNotFound") req = &eventsproto.EventRequest{ SessionToken: "validToken", UUID: "/redfish/v1/Systems/abab09db-e7a9-4352-8df0-5e41315a2a4c.1", } - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req, "1225122") assert.Equal(t, http.StatusBadRequest, int(resp.StatusCode), "Status Code should be StatusNotFound") GetIPFromHostNameFunc = func(fqdn string) (string, error) { return "", fmt.Errorf("Not Found") } - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req, "1225122") assert.Equal(t, http.StatusNotFound, int(resp.StatusCode), "Status Code should be ResourceNotFound") GetIPFromHostNameFunc = func(fqdn string) (string, error) { @@ -150,7 +150,7 @@ func TestDeleteEventSubscriptionOnDeletedServer(t *testing.T) { pc.DB.GetEvtSubscriptions = func(s string) ([]evmodel.SubscriptionResource, error) { return nil, &errors.Error{} } - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req, "1225122") assert.Equal(t, http.StatusNotFound, int(resp.StatusCode), "Status Code should be ResourceNotFound") pc = getMockMethods() @@ -163,7 +163,7 @@ func TestDeleteEventSubscriptionOnDeletedServer(t *testing.T) { SessionToken: "validToken", UUID: "/redfish/v1/Systems/6d4a0a66-7efa-578e-83cf-44dc68d2874e.1", } - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req, "1225122") assert.Equal(t, http.StatusInternalServerError, int(resp.StatusCode), "Status Code should be StatusNoContent") DecryptWithPrivateKeyFunc = func(ciphertext []byte) ([]byte, error) { @@ -172,14 +172,14 @@ func TestDeleteEventSubscriptionOnDeletedServer(t *testing.T) { pc.DB.GetDeviceSubscriptions = func(s string) (*common.DeviceSubscription, error) { return nil, &errors.Error{} } - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req, "1225122") assert.Equal(t, http.StatusBadRequest, int(resp.StatusCode), "Status Code should be StatusNotFound") pc = getMockMethods() pc.DB.DeleteEvtSubscription = func(s string) error { return &errors.Error{} } - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req, "1225122") assert.Equal(t, http.StatusBadRequest, int(resp.StatusCode), "Status Code should be StatusNoContent") pc = getMockMethods() @@ -188,7 +188,7 @@ func TestDeleteEventSubscriptionOnDeletedServer(t *testing.T) { } req.UUID = "/redfish/v1/Systems/6d4a0a66-7efa-578e-83cf-44dc68d2874d.1" - resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req) + resp = pc.DeleteEventSubscriptions(evcommon.MockContext(), req, "1225122") assert.Equal(t, http.StatusBadRequest, int(resp.StatusCode), "Status Code should be StatusNoContent") } @@ -206,7 +206,7 @@ func TestDeleteEventSubscriptionOnFabrics(t *testing.T) { SessionToken: "validToken", EventSubscriptionID: "71de0110-c35a-4859-984c-072d6c5a32d9", } - resp := pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req) + resp := pc.DeleteEventSubscriptionsDetails(evcommon.MockContext(), req, "admin", "test") assert.Equal(t, http.StatusOK, int(resp.StatusCode), "Status Code should be StatusOK") } @@ -376,9 +376,9 @@ func TestExternalInterfaces_resubscribeFabricsSubscription(t *testing.T) { config.SetUpMockConfig(t) pc := getMockMethods() event := model.EventDestination{} - err := pc.resubscribeFabricsSubscription(evcommon.MockContext(), event, "/fabric/scascascsaa", false) + err := pc.resubscribeFabricsSubscription(evcommon.MockContext(), event, "/fabric/scascascsaa", "admin", "test", false) assert.Nil(t, err) - err = pc.resubscribeFabricsSubscription(evcommon.MockContext(), event, "/redfish/v1/Fabrics/6d4a0a66-7efa-578e-83cf-44dc68d2874e", false) + err = pc.resubscribeFabricsSubscription(evcommon.MockContext(), event, "/redfish/v1/Fabrics/6d4a0a66-7efa-578e-83cf-44dc68d2874e", "admin", "test", false) assert.Nil(t, err) } @@ -387,7 +387,7 @@ func TestExternalInterfaces_subscribe(t *testing.T) { pc := getMockMethods() event := model.EventDestination{} - err := pc.subscribe(evcommon.MockContext(), event, "/redfish/v1/Systems/6d4a0a66-7efa-578e-83cf-44dc68d2874e.1", false, "valid") + _, err := pc.subscribe(evcommon.MockContext(), event, "/redfish/v1/Systems/6d4a0a66-7efa-578e-83cf-44dc68d2874e.1", false, "valid", "admin", "test", "test") assert.Nil(t, err) } diff --git a/svc-events/events/evtsubscription.go b/svc-events/events/evtsubscription.go index 9a60757a2..89d2a5d37 100644 --- a/svc-events/events/evtsubscription.go +++ b/svc-events/events/evtsubscription.go @@ -817,6 +817,30 @@ func (e *ExternalInterfaces) DeleteSubscriptions(ctx context.Context, originReso return resp, nil } +// DeleteSubscriptions will delete subscription from device +func (e *ExternalInterfaces) GetSubscriptionLocation(ctx context.Context, target *common.Target) error { + var err error + var deviceSubscription *common.DeviceSubscription + addr, err := common.GetIPFromHostName(target.ManagerAddress) + if err != nil { + return err + } + searchKey := evcommon.GetSearchKey(addr, evmodel.DeviceSubscriptionIndex) + deviceSubscription, err = e.GetDeviceSubscriptions(searchKey) + + if err != nil { + // if its first subscription then no need to check events subscribed + if strings.Contains(err.Error(), "No data found for the key") { + return nil + } + errorMessage := "Error while get subscription details: " + err.Error() + + l.LogWithFields(ctx).Error(errorMessage) + return err + } + target.Location = deviceSubscription.Location + return nil +} func (e *ExternalInterfaces) createEventSubscription(ctx context.Context, taskID string, subTaskID string, subTaskChan chan<- int32, reqSessionToken string, targetURI string, request model.EventDestination, originResource string, result *evresponse.MutexLock, wg *sync.WaitGroup, collectionFlag bool, collectionName string, aggregateResource string, isAggregateCollection bool) { @@ -870,8 +894,8 @@ func (e *ExternalInterfaces) createEventSubscription(ctx context.Context, taskID } // CreateSubTask creates a child task for a task calling RPC to task service and returns the subtask ID -func (e *ExternalInterfaces) CreateSubTask(ctx context.Context, reqSessionToken string, parentTask string) string { - subTaskURI, err := e.CreateChildTask(ctx, reqSessionToken, parentTask) +func (e *ExternalInterfaces) CreateSubTask(ctx context.Context, sessionUserName string, parentTask string) string { + subTaskURI, err := e.CreateChildTask(ctx, sessionUserName, parentTask) if err != nil { l.LogWithFields(ctx).Error("Error while creating the SubTask") } diff --git a/svc-events/evresponse/events_test.go b/svc-events/evresponse/events_test.go index 370eed5cd..d0eab8d2d 100644 --- a/svc-events/evresponse/events_test.go +++ b/svc-events/evresponse/events_test.go @@ -32,7 +32,7 @@ func TestResponse(t *testing.T) { "37646c88-a7d7-468c-af58-49e8a0adbbb2.1", } var hosts = []string{"10.10.10.10", "10.10.10.11", "10.10.10.12"} - var responses = []EventResponse{{StatusCode: 201}, {StatusCode: 400}, {StatusCode: 201}} + var responses = []EventResponse{{StatusCode: 202}, {StatusCode: 400}, {StatusCode: 202}} var result = &MutexLock{ Response: make(map[string]EventResponse), Lock: &sync.Mutex{}, diff --git a/svc-events/rpc/common.go b/svc-events/rpc/common.go new file mode 100644 index 000000000..45fbef23d --- /dev/null +++ b/svc-events/rpc/common.go @@ -0,0 +1,69 @@ +package rpc + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "strings" + + "github.com/ODIM-Project/ODIM/lib-utilities/common" + l "github.com/ODIM-Project/ODIM/lib-utilities/logs" + eventsproto "github.com/ODIM-Project/ODIM/lib-utilities/proto/events" + "github.com/ODIM-Project/ODIM/lib-utilities/response" +) + +func (e *Events) AuthorizeAndCreateTask(ctx context.Context, sessionToken string, + resp *eventsproto.EventSubResponse) (string, string, error) { + + var ( + err error + taskID, taskURI, sessionUserName string + ) + + // Athorize the request here + authResp, err := e.Connector.Auth(ctx, sessionToken, []string{common.PrivilegeConfigureComponents}, []string{}) + if authResp.StatusCode != http.StatusOK { + errMsg := fmt.Sprintf("error while trying to authenticate session: status code: %v, status message: %v", + authResp.StatusCode, authResp.StatusMessage) + if err != nil { + errMsg = errMsg + ": " + err.Error() + } + l.LogWithFields(ctx).Error(errMsg) + resp.Body = generateResponse(ctx, authResp.Body) + resp.StatusCode = authResp.StatusCode + return sessionUserName, taskID, fmt.Errorf(errMsg) + } + + sessionUserName, err = e.Connector.GetSessionUserName(ctx, sessionToken) + if err != nil { + errorMessage := "error while trying to get the session username: " + err.Error() + resp.Body = generateResponse(ctx, common.GeneralError(http.StatusUnauthorized, + response.NoValidSession, errorMessage, nil, nil)) + resp.StatusCode = http.StatusUnauthorized + l.LogWithFields(ctx).Error(errorMessage) + return sessionUserName, taskID, err + } + // Create the task and get the taskID + // Contact Task Service using RPC and get the taskID + taskURI, err = e.Connector.CreateTask(ctx, sessionUserName) + if err != nil { + // print err here as we are unbale to contact svc-task service + errorMessage := "error while trying to create the task: " + err.Error() + resp.StatusCode = http.StatusInternalServerError + resp.StatusMessage = response.InternalError + resp.Body, _ = json.Marshal(common.GeneralError(http.StatusInternalServerError, + response.InternalError, errorMessage, nil, nil).Body) + l.LogWithFields(ctx).Error(errorMessage) + return sessionUserName, taskID, fmt.Errorf(errorMessage) + } + + taskID = strings.TrimPrefix(taskURI, "/redfish/v1/TaskService/Tasks/") + resp.StatusCode = http.StatusAccepted + resp.Header = map[string]string{ + "Location": "/taskmon/" + taskID, + } + resp.StatusMessage = response.TaskStarted + generateTaskResponse(ctx, taskID, taskURI, resp) + return sessionUserName, taskID, nil +} diff --git a/svc-events/rpc/events.go b/svc-events/rpc/events.go index 06912ce3c..c13bab571 100644 --- a/svc-events/rpc/events.go +++ b/svc-events/rpc/events.go @@ -212,61 +212,14 @@ func (e *Events) GetEventService(ctx context.Context, req *eventsproto.EventSubR // The functionality is to create the subscription with Resource provided in origin resources. func (e *Events) CreateEventSubscription(ctx context.Context, req *eventsproto.EventSubRequest) (*eventsproto.EventSubResponse, error) { var resp eventsproto.EventSubResponse - var err error - var taskID string ctx = common.GetContextData(ctx) ctx = common.ModifyContext(ctx, common.EventService, podName) - - // Athorize the request here - authResp, err := e.Connector.Auth(ctx, req.SessionToken, []string{common.PrivilegeConfigureComponents}, []string{}) - if authResp.StatusCode != http.StatusOK { - errMsg := fmt.Sprintf("error while trying to authenticate session: status code: %v, status message: %v", - authResp.StatusCode, authResp.StatusMessage) - if err != nil { - errMsg = errMsg + ": " + err.Error() - } - l.LogWithFields(ctx).Error(errMsg) - resp.Body = generateResponse(ctx, authResp.Body) - resp.StatusCode = authResp.StatusCode - return &resp, nil - } - sessionUserName, err := e.Connector.GetSessionUserName(ctx, req.SessionToken) + sessionUserName, taskID, err := e.AuthorizeAndCreateTask(ctx, req.SessionToken, &resp) if err != nil { - errorMessage := "error while trying to get the session username: " + err.Error() - resp.Body = generateResponse(ctx, common.GeneralError(http.StatusUnauthorized, - response.NoValidSession, errorMessage, nil, nil)) - resp.StatusCode = http.StatusUnauthorized - l.LogWithFields(ctx).Error(errorMessage) return &resp, err } - // Create the task and get the taskID - // Contact Task Service using RPC and get the taskID - taskURI, err := e.Connector.CreateTask(ctx, sessionUserName) - if err != nil { - // print err here as we are unbale to contact svc-task service - errorMessage := "error while trying to create the task: " + err.Error() - resp.StatusCode = http.StatusInternalServerError - resp.StatusMessage = response.InternalError - resp.Body, _ = json.Marshal(common.GeneralError(http.StatusInternalServerError, - response.InternalError, errorMessage, nil, nil).Body) - l.LogWithFields(ctx).Error(errorMessage) - return &resp, fmt.Errorf(resp.StatusMessage) - } - strArray := strings.Split(taskURI, "/") - if strings.HasSuffix(taskURI, "/") { - taskID = strArray[len(strArray)-2] - } else { - taskID = strArray[len(strArray)-1] - } //Spawn the thread to process the action asynchronously go e.Connector.CreateEventSubscription(ctx, taskID, sessionUserName, req) - // Return 202 accepted - resp.StatusCode = http.StatusAccepted - resp.Header = map[string]string{ - "Location": "/taskmon/" + taskID, - } - resp.StatusMessage = response.TaskStarted - generateTaskResponse(ctx, taskID, taskURI, &resp) return &resp, nil } @@ -349,28 +302,42 @@ func (e *Events) GetEventSubscription(ctx context.Context, req *eventsproto.Even func (e *Events) DeleteEventSubscription(ctx context.Context, req *eventsproto.EventRequest) (*eventsproto.EventSubResponse, error) { ctx = common.GetContextData(ctx) ctx = common.ModifyContext(ctx, common.EventService, podName) - var resp eventsproto.EventSubResponse - var err error - var data response.RPC + var ( + // taskID string + resp eventsproto.EventSubResponse + ) + if req.UUID == "" { // Delete Event Subscription when admin requested - data = e.Connector.DeleteEventSubscriptionsDetails(ctx, req) + sessionUserName, taskID, err := e.AuthorizeAndCreateTask(ctx, req.SessionToken, &resp) + if err != nil { + return &resp, err + } + go e.Connector.DeleteEventSubscriptionsDetails(ctx, req, sessionUserName, taskID) } else { // Delete Event Subscription to Device when Server get Deleted - data = e.Connector.DeleteEventSubscriptions(ctx, req) - } - resp.Body, err = JSONMarshal(data.Body) - if err != nil { - errorMessage := "error while trying marshal the response body for delete event subsciption : " + err.Error() - resp.StatusCode = http.StatusInternalServerError - resp.StatusMessage = response.InternalError - resp.Body, _ = json.Marshal(common.GeneralError(http.StatusInternalServerError, response.InternalError, errorMessage, nil, nil).Body) - l.LogWithFields(ctx).Error(resp.StatusMessage) - return &resp, nil + // Task Service using RPC and get the taskID + taskURI, err := e.Connector.CreateTask(ctx, req.SessionToken) + if err != nil { + resp = eventsproto.EventSubResponse{} + errMsg := "Unable to create task: " + err.Error() + // // generateResponse(common.GeneralError(http.StatusInternalServerError, response.InternalError, errMsg, nil, nil), resp) + l.LogWithFields(ctx).Error(errMsg) + return &resp, nil + } + var taskID string + strArray := strings.Split(taskURI, "/") + if strings.HasSuffix(taskURI, "/") { + taskID = strArray[len(strArray)-2] + } else { + taskID = strArray[len(strArray)-1] + } + resp.StatusCode = http.StatusAccepted + resp.Header = map[string]string{ + "Location": "/taskmon/" + taskID, + } + go e.Connector.DeleteEventSubscriptions(ctx, req, taskID) } - resp.StatusCode = data.StatusCode - resp.StatusMessage = data.StatusMessage - resp.Header = data.Header return &resp, nil } @@ -462,3 +429,9 @@ func (e *Events) UpdateSubscriptionLocationRPC(ctx context.Context, in *eventspr resp.Status = isUpdated return &resp, nil } + +func (e *Events) DeleteSubscription(ctx context.Context, in *eventsproto.EventRequest) (*eventsproto.EventSubResponse, error) { + var resp eventsproto.EventSubResponse + err := e.Connector.DeleteEvtSubscription(in.EventSubscriptionID) + return &resp, err +} diff --git a/svc-events/rpc/events_test.go b/svc-events/rpc/events_test.go index 2b6f18cbb..a894f715e 100644 --- a/svc-events/rpc/events_test.go +++ b/svc-events/rpc/events_test.go @@ -244,17 +244,17 @@ func TestDeleteEventSubscription(t *testing.T) { resp, err := events.DeleteEventSubscription(evcommon.MockContext(), req) assert.Nil(t, err, "There should be no error") - assert.Equal(t, int(resp.StatusCode), http.StatusOK, "Status code should be StatusOK.") + assert.Equal(t, http.StatusAccepted, int(resp.StatusCode), "Status code should be Accepted.") req.EventSubscriptionID = "81de0110" delResp, _ := events.DeleteEventSubscription(evcommon.MockContext(), req) - assert.Equal(t, int(delResp.StatusCode), http.StatusNotFound, "Status code should be StatusNotFound.") + assert.Equal(t, int(delResp.StatusCode), http.StatusAccepted, "Status code should be StatusNotFound.") JSONMarshal = func(v interface{}) ([]byte, error) { return nil, fmt.Errorf("") } resp, err = events.DeleteEventSubscription(evcommon.MockContext(), req) assert.Nil(t, err, "There should be an error") - assert.Equal(t, int(resp.StatusCode), http.StatusInternalServerError, "Status code should be StatusInternalServerError.") + assert.Equal(t, http.StatusAccepted, int(resp.StatusCode), "Status code should be StatusInternalServerError.") JSONMarshal = func(v interface{}) ([]byte, error) { return json.Marshal(v) } } @@ -264,18 +264,18 @@ func TestDeleteEventSubscriptionwithUUID(t *testing.T) { events := getMockPluginContactInitializer() // Positive test cases req := &eventsproto.EventRequest{ - SessionToken: "validToken", + SessionToken: "admin", UUID: "/redfish/v1/Systems/6d4a0a66-7efa-578e-83cf-44dc68d2874e.1", } resp, err := events.DeleteEventSubscription(evcommon.MockContext(), req) assert.Nil(t, err, "There should be no error") - assert.Equal(t, int(resp.StatusCode), http.StatusNoContent, "Status code should be StatusNoContent.") + assert.Equal(t, http.StatusAccepted, int(resp.StatusCode), "Status code should be StatusAccepted.") req.UUID = "81de0110" delResp, _ := events.DeleteEventSubscription(evcommon.MockContext(), req) - assert.Equal(t, int(delResp.StatusCode), http.StatusBadRequest, "Status code should be StatusBadRequest.") + assert.Equal(t, http.StatusAccepted, int(delResp.StatusCode), "Status code should be StatusAccepted.") } func TestCreateDefaultSubscriptions(t *testing.T) { diff --git a/svc-managers/managers/managers.go b/svc-managers/managers/managers.go index 85e7ed8d1..d2fd34b02 100644 --- a/svc-managers/managers/managers.go +++ b/svc-managers/managers/managers.go @@ -289,7 +289,9 @@ func (e *ExternalInterface) GetManagersResource(ctx context.Context, req *manage } errorMessage := "unable to get odimra managers details: " + err.Error() l.LogWithFields(ctx).Error(errorMessage) - return common.GeneralError(http.StatusInternalServerError, response.InternalError, errorMessage, []interface{}{}, nil) + errArgs := []interface{}{tableName, req.ManagerID} + return common.GeneralError(http.StatusNotFound, response.ResourceNotFound, errorMessage, errArgs, nil) + } json.Unmarshal([]byte(data), &resource) diff --git a/svc-task/tcommon/common.go b/svc-task/tcommon/common.go index 75f2572e4..f4c2b7bc7 100644 --- a/svc-task/tcommon/common.go +++ b/svc-task/tcommon/common.go @@ -131,6 +131,26 @@ func UpdateSubscriptionLocation(ctx context.Context, location, host string) { l.LogWithFields(ctx).Debug("Location update status ", isUpdated) } +// DeleteSubscription do the RPC call to events service to delete subscription +// once a delete event subscription is completed +func DeleteSubscription(ctx context.Context, subscriptionID string) { + conn, err := services.ODIMService.Client(services.Events) + if err != nil { + l.LogWithFields(ctx).Error("Error while Event ", err.Error()) + return + } + defer conn.Close() + event := eventproto.NewEventsClient(conn) + _, err = event.DeleteSubscription(ctx, &eventproto.EventRequest{ + EventSubscriptionID: subscriptionID, + }) + if err != nil { + l.LogWithFields(ctx).Info("Error while delete subscription ", err) + return + } + l.LogWithFields(ctx).Debug("Subscription delete completed ", subscriptionID) +} + // UpdateAccount do the RPC call to events service to update account details func UpdateRemoteAccount(ctx context.Context, location, host string) { conn, err := services.ODIMService.Client(services.Managers) diff --git a/svc-task/thandle/thandle.go b/svc-task/thandle/thandle.go index f5e7e90a0..66b27ca83 100644 --- a/svc-task/thandle/thandle.go +++ b/svc-task/thandle/thandle.go @@ -32,6 +32,7 @@ import ( l "github.com/ODIM-Project/ODIM/lib-utilities/logs" taskproto "github.com/ODIM-Project/ODIM/lib-utilities/proto/task" "github.com/ODIM-Project/ODIM/lib-utilities/response" + "github.com/ODIM-Project/ODIM/lib-utilities/services" "github.com/ODIM-Project/ODIM/svc-task/tcommon" "github.com/ODIM-Project/ODIM/svc-task/tmodel" "github.com/ODIM-Project/ODIM/svc-task/tresponse" @@ -1252,6 +1253,9 @@ func (ts *TasksRPC) updateParentTask(ctx context.Context, taskID, taskStatus, ta parentTask.TaskFinalResponse = nil parentTask.StatusCode = http.StatusCreated } + if value, err := services.GetEventSubscriptionID(ctx, parentTask.ID); err == nil { + tcommon.DeleteSubscription(ctx, value) + } ts.updateTaskToCompleted(parentTask) return nil } @@ -1299,6 +1303,9 @@ func (ts *TasksRPC) validateChildTasksAndUpdateParentTask(ctx context.Context, c parentTask.TaskFinalResponse = nil parentTask.StatusCode = http.StatusCreated } + if value, err := services.GetEventSubscriptionID(ctx, parentTask.ID); err == nil { + tcommon.DeleteSubscription(ctx, value) + } ts.updateTaskToCompleted(parentTask) }