/// <summary> /// command specific implementation /// </summary> internal override void commandImplementation() { MultiRecordResponse myResponse = new MultiRecordResponse(); try { dynamic payload = null; if (!(this.argVal.ContainsKey('p'))) { Console.WriteLine(WcsCliConstants.commandFailure + " No record data specified, please look at command help"); return; } else { this.argVal.TryGetValue('p', out payload); } myResponse = WcsCli2CmConnectionManager.channel.SetPdbAssetInfo((string)payload); } catch (Exception ex) { SharedFunc.ExceptionOutput(ex); return; } ResponseValidation.ValidateResponse("PDB's FRU has been written successfully", myResponse, true); }
/// <summary> /// Prepares and writes the Multi Record area portion of FRU for blade, CM and Pdb. /// </summary> /// <param name="deviceId"></param> /// <param name="recordData"></param> /// <param name="deviceType"></param> /// <returns></returns> private MultiRecordResponse WriteMultiRecordFru(int deviceId, string recordData, DeviceType deviceType) { Ipmi.FruCommonHeader commonHeader = null; Ipmi.FruMultiRecordInfo multiRecordInfo = null; CompletionCode completionCode = CompletionCode.UnspecifiedError; switch (deviceType) { case DeviceType.Server: { Ipmi.FruDevice fruData = WcsBladeFacade.GetFruDeviceInfo((byte)deviceId); completionCode = (CompletionCode)fruData.CompletionCode; commonHeader = fruData.CommonHeader; multiRecordInfo = fruData.MultiRecordInfo; } break; case DeviceType.ChassisFruEeprom: case DeviceType.PdbFruEeprom: { Ipmi.FruDevice fruData = ChassisState.CmFruData.ReadFru(deviceType); completionCode = (CompletionCode)fruData.CompletionCode; commonHeader = fruData.CommonHeader; multiRecordInfo = fruData.MultiRecordInfo; } break; default: Tracer.WriteInfo("WriteMultiRecordFru() Unknown device type: {0} CompletionCode: {1}", deviceType.ToString(), completionCode.ToString()); break; } MultiRecordResponse setAsset = new MultiRecordResponse(); try { if (completionCode == (byte)CompletionCode.Success) { if (multiRecordInfo == null || commonHeader == null) { Tracer.WriteError("WriteMultiRecordFru(). multiRecordInfo and/or commonHeader is null. Aborting..."); setAsset.completionCode = Contracts.CompletionCode.Failure; setAsset.statusDescription = setAsset.completionCode.ToString(); return setAsset; } // Abort if the starting offset of MultiRecordArea is 0, // suggesting that no Multi Record Area provision is made if (commonHeader.MultiRecordAreaStartingOffset == 0) { Tracer.WriteError(@"WriteMultiRecordFru(). MultiRecordAreaStartingOffset is zero. Fru does not have Multi Record Area provisioned."); setAsset.completionCode = Contracts.CompletionCode.WriteFruZeroStartingOffset; setAsset.statusDescription = setAsset.completionCode.ToString(); return setAsset; } int idx = 0; // offset for different Multi Record Area fields (including header) byte[] header = new byte[multiRecordInfo.HeaderSize]; byte maxWritesRemaining = 255; bool writesRemainingReset = false; // to track if writes remaining has been set to the max value // When starting offset of Multi Record Area is not 0, and both Record ID, Record Format is not set, // it implies that the Multi Record Area is being written for the first time. // The Multi Record Area format is as follows: [0] Record Type ID, [1] Record Format, // [2] Record Length, [3] Record Checksum, [4] Header Checksum, [5-7] Manufacturer ID, // [8] Language Code, [9] Writes Remaining, [10-N]: Record Data // 0x1 is the header record format as per the specification. if (commonHeader.MultiRecordAreaStartingOffset != 0 && multiRecordInfo.RecordTypeId != Ipmi.FruMultiRecordInfo.MultiRecordId && multiRecordInfo.RecordFormat != (byte)1) { // We are writing to Multi Record Area for the first time. // Prepare and populate the Multi Record Header. // We increment idx depending on the length of each header field. See IPMI FRU Specs. for // details on the header format including length and offset of fields. // Add Record Type Id at index [0] header[idx++] = Ipmi.FruMultiRecordInfo.MultiRecordId; // Add Record Format at index [1] header[idx++] = 0x1; // Add Writes Remaining at index [9]. This is custom defined maximum number of writes allowed. header[idx + 7] = maxWritesRemaining; // writes remaining index is at index [9] writesRemainingReset = true; } else // There is already an existing Multi Record Area portion of FRU { // Abort, if number of writes remaining is not greater than 0 if (!(multiRecordInfo.WritesRemaining > 0)) { if (!ConfigLoaded.ResetMultiRecordFruWritesRemaining) { Tracer.WriteInfo("WriteMultiRecordFru(). Maximum allowable writes limit reached. Aborting..."); setAsset.completionCode = Contracts.CompletionCode.WriteFruZeroWritesRemaining; setAsset.statusDescription = setAsset.completionCode.ToString(); return setAsset; } else { Tracer.WriteInfo("WriteMutliRecordFru(). Resetting the maximum writes remaining to default"); header[idx + 9] = maxWritesRemaining; writesRemainingReset = true; } } // Add Record Type Id at index [0] header[idx++] = multiRecordInfo.RecordTypeId; // Read from existing Record Format at index [1] header[idx++] = multiRecordInfo.RecordFormat; } byte[] record = PrepareRecordData(recordData); if (record == null) { record = new byte[] { }; // create a dummy empty record byte array } // Add Record Length at index [2] // This is for 8-bit ASCII + Latin 1. int manufacturerIdLen = multiRecordInfo.ManufacturerIdLen; // Specification defined Manufacturer ID length int recordLength = (multiRecordInfo.HeaderSize + record.Length); // If the length is not multiple of 8, extend length to next multiple of 8 if ((recordLength) % 8 != 0) recordLength += recordLength % 8; // The length has to be multiple of 8 bytes header[idx++] = (byte)(recordLength / 8); // Add Record Checksum [3] header[idx++] = Ipmi.IpmiSharedFunc.TwoComplementChecksum(0, record.Length, record); // Add Header Checksum [4] header[idx++] = Ipmi.IpmiSharedFunc.TwoComplementChecksum(0, header.Length, header); // Add Manufacturer ID at indices [5-7] // Manufacturer ID is pre-defined and set to Microsoft byte[] manufacturerIdArray = ConfigLoaded.MultiRecordFruManufacturerId; System.Buffer.BlockCopy(manufacturerIdArray, 0, header, idx, manufacturerIdArray.Length); idx += manufacturerIdLen; // Add Language Code at index [8] // 0 represents 'English' language code header[idx++] = 0x0; // Decrement WritesRemaining at index [9] if WritesRemaining is not reset to max value if (!writesRemainingReset && multiRecordInfo.WritesRemaining > 0) { header[idx++] = (byte)(multiRecordInfo.WritesRemaining - 1); } // Add Record Data to Multi Record Area byte[] writePayLoad = new byte[header.Length + record.Length]; System.Buffer.BlockCopy(header, 0, writePayLoad, 0, header.Length); System.Buffer.BlockCopy(record, 0, writePayLoad, header.Length, record.Length); // Maximum allowable bytes for the Multi Record Area portion (max. length allowed based on specification) // It includes 10 bytes for the header, + 1 length byte, + 56 bytes for the max field size, // + 1 length byte, + 56 bytes for the second max field size, + 1 end of field byte. // This totals as 125 bytes. However, the MultiRecord Area length needs to be a multiple of 8 bytes. // So if maxBytes < 128, the entire payload size follows the limit. // Increasing higher than this number though won't change anything because the entire payload size // to send to the FRU has already been created. Thus, we are keeping it at 126. const int maxBytes = 128; if (writePayLoad.Length < maxBytes) { ushort offset = commonHeader.MultiRecordAreaStartingOffset; int allowedWrites = 16; int writes = 0; int writeIdx = 0; int maxPayLoadSize = 16; ushort writeOffset = offset; try { // Since, Ipmi send/receive operates at granularity of 16 bytes chunk, we iterate over the // entire payload length to send the full payload. while ((writeOffset < writePayLoad.Length + offset) && (allowedWrites > writes)) { ushort diff = (ushort)(writePayLoad.Length - writeIdx); if (diff < maxPayLoadSize) maxPayLoadSize = diff; byte[] payLoad = new byte[maxPayLoadSize]; // writes to Ipmi are at the granularity of 16 bytes payload System.Buffer.BlockCopy(writePayLoad, writeIdx, payLoad, 0, payLoad.Length); if (deviceType == DeviceType.ChassisFruEeprom || deviceType == DeviceType.PdbFruEeprom) { completionCode = ChassisState.CmFruData.WriteChassisFru(writeOffset, (ushort)payLoad.Length, payLoad, deviceType); } else if (deviceType == DeviceType.Server) { byte rawCompletionCode = WcsBladeFacade.WriteFruDevice((byte)deviceId, writeOffset, payLoad).CompletionCode; // if write fru failed if (rawCompletionCode != (byte)CompletionCode.Success) { if (Enum.IsDefined(typeof(CompletionCode), rawCompletionCode)) { // convert Ipmi completion code byte to known Chassis Manager // completion code enum. completionCode = (CompletionCode)rawCompletionCode; } else { // unable to convert Ipmi completion code "byte" to Chassis Manager // completion code enum. completionCode = CompletionCode.IpmiInvalidCommand; } } // else successful completion code else { completionCode = CompletionCode.Success; } } else { completionCode = CompletionCode.UnspecifiedError; Tracer.WriteError(string.Format( "WriteMultiRecordFru() Unknown Device Type. The device type: {0}", deviceType)); break; } if (completionCode != CompletionCode.Success) { Tracer.WriteInfo("WriteMultiRecordFru(). FRU Write unsuccessful, so breaking out of write while loop"); break; } // Increment offset/index to account for the next 16 byte chunk of payload writeOffset += (ushort)maxPayLoadSize; writeIdx += maxPayLoadSize; writes++; if (writes > allowedWrites) { Tracer.WriteInfo(@"WriteMultiRecordFru(). Number of FRU writes have been exhausted. The data is greater than writes by max allowed writes"); } } } catch (Exception ex) { Tracer.WriteError("WriteMultiRecordFru(). Exception occcurred while writing to FRU: " + ex); } if (completionCode == CompletionCode.Success) { setAsset.completionCode = Contracts.CompletionCode.Success; } else { setAsset.completionCode = ChassisManagerUtil.GetContractsCompletionCodeMapping((byte)completionCode); setAsset.statusDescription = setAsset.completionCode.ToString(); } } else { Tracer.WriteError("WriteMultiRecordFru(). Maximum bytes reached and cannot write."); setAsset.completionCode = Contracts.CompletionCode.WriteFruMaxRecordSizeReached; setAsset.statusDescription = setAsset.completionCode.ToString(); return setAsset; } } else { Tracer.WriteInfo(string.Format("WriteMultiRecordFru(). Write FRU returned non-success completion code: {0}", completionCode.ToString())); setAsset.completionCode = Contracts.CompletionCode.Failure; return setAsset; } } catch (Exception ex) { Tracer.WriteError("WriteMultiRecordFru(). Exception occurred in WriteMultiRecordFru method with details " + ex.Message); setAsset.completionCode = Contracts.CompletionCode.Failure; setAsset.statusDescription = setAsset.completionCode.ToString(); } return setAsset; }
private void VerifyCmOrPdbFruWritesRemaining(ref bool allPassed, bool verifyingSetCmAssetInfo) { bool testPassed; string currentApi = verifyingSetCmAssetInfo ? "SetChassisManagerAssetInfo" : "SetPdbAssetInfo"; MultiRecordResponse cmOrPdbAssetInfo = new MultiRecordResponse(); // Initialize FRU Writes Remaining Dictionary KeyValue Pair Dictionary<string, string> fruWritesRemainingKeyValue = new Dictionary<string, string> { {"ResetMultiRecordFruWritesRemaining", "0"} }; // Set Channel for WcsCmAdmin User this.TestChannelContext = this.ListTestChannelContexts[(int)WCSSecurityRole.WcsCmAdmin]; if (!ConfigureAppConfig(fruWritesRemainingKeyValue, false)) { CmTestLog.Failure(string.Format("{0}: Setting App.Config failed for KeyValue '{1},{2}'", currentApi, fruWritesRemainingKeyValue.Keys.First(), fruWritesRemainingKeyValue.Values.First())); allPassed = false; return; } // Restart CM Service if (!RestartCmService(currentApi)) { allPassed = false; return; } // Fail FRU Writes Remaining before starting test int writeCount = 0; while (cmOrPdbAssetInfo.completionCode != CompletionCode.WriteFruZeroWritesRemaining && writeCount < (AssetManagementConstants.fruWritesRemaining + 1)) { if (verifyingSetCmAssetInfo) cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty); else cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty); if ((cmOrPdbAssetInfo.completionCode != CompletionCode.Success) && (cmOrPdbAssetInfo.completionCode != CompletionCode.WriteFruZeroWritesRemaining)) { CmTestLog.Failure(string.Format("{0}: Command returned Completion Code {1} while trying to return {2}", currentApi, Enum.GetName(typeof(CompletionCode), cmOrPdbAssetInfo.completionCode), "WriteFruZeroWritesRemaining")); allPassed = false; return; } writeCount++; } if (cmOrPdbAssetInfo.completionCode != CompletionCode.WriteFruZeroWritesRemaining) { CmTestLog.Failure(string.Format("{0}: Command returned Completion Code {1} while trying to return {2}", currentApi, Enum.GetName(typeof(CompletionCode), cmOrPdbAssetInfo.completionCode), "WriteFruZeroWritesRemaining")); allPassed = false; return; } CmTestLog.Info(string.Format("{0}: FRU Writes Remaining set to 0", currentApi)); // Restore App.Config with Original Values if (!ConfigureAppConfig(fruWritesRemainingKeyValue, true)) { CmTestLog.Failure(string.Format("{0}: App.Config cleanup failed", currentApi)); allPassed = false; return; } // Restart CM Service if (!RestartCmService(currentApi)) { allPassed = false; return; } // Verify SetChassisManagerAssetInfo resets number of writes to default value after configuring App.Config : WorkItem(8709) // Verify SetPdbAssetInfo resets number of writes to default value after configuring App.Config : WorkItem(10173) // Set App.Config key "ResetMultiRecordFruWritesRemaining" to value "1" fruWritesRemainingKeyValue["ResetMultiRecordFruWritesRemaining"] = "1"; if (!ConfigureAppConfig(fruWritesRemainingKeyValue, false)) { CmTestLog.Failure(string.Format("{0}: Setting App.Config failed for KeyValue '{1},{2}'", currentApi, fruWritesRemainingKeyValue.Keys.First(), fruWritesRemainingKeyValue.Values.First())); allPassed = false; return; } // Restart CM Service if (!RestartCmService(currentApi)) { allPassed = false; return; } if (verifyingSetCmAssetInfo) cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty); else cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty); testPassed = ChassisManagerTestHelper.AreEqual(CompletionCode.Success, cmOrPdbAssetInfo.completionCode, string.Format("{0}: Command resets number of writes to default value", currentApi)); allPassed &= testPassed; // Restore App.Config with Original Values if (!ConfigureAppConfig(fruWritesRemainingKeyValue, true)) { CmTestLog.Failure(string.Format("{0}: App.Config cleanup failed", currentApi)); allPassed = false; return; } if (!allPassed) { RestartCmService(currentApi); return; } // Starting test: Set command fails for attempting to set payload with 0 writes remaining // Set App.Config key "ResetMultiRecordFruWritesRemaining" to value "0" fruWritesRemainingKeyValue["ResetMultiRecordFruWritesRemaining"] = "0"; if (!ConfigureAppConfig(fruWritesRemainingKeyValue, false)) { CmTestLog.Failure(string.Format("{0}: Setting App.Config failed for KeyValue '{1},{2}'", currentApi, fruWritesRemainingKeyValue.Keys.First(), fruWritesRemainingKeyValue.Values.First())); allPassed = false; return; } // Restart CM Service if (!RestartCmService(currentApi)) { allPassed = false; return; } // Verify SetChassisManagerAssetInfo command should return WritFruZeroWritesRemaining after 256 iterations : WorkItem(4711) // Verify SetPdbAssetInfo command should return WritFruZeroWritesRemaining after 256 iterations : WorkItem(4713) for (int callApiCount = 0; callApiCount < 255; callApiCount++) { if (verifyingSetCmAssetInfo) cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty); else cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty); if (cmOrPdbAssetInfo.completionCode != CompletionCode.Success) { CmTestLog.Failure(string.Format("{0}: Command returns Completion Code {1} on callApiCount {2} before {3}", currentApi, Enum.GetName(typeof(CompletionCode), cmOrPdbAssetInfo.completionCode), callApiCount, "WriteFruZeroWritesRemaining")); allPassed = false; return; } } if (verifyingSetCmAssetInfo) cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty); else cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty); testPassed = ChassisManagerTestHelper.AreEqual(CompletionCode.WriteFruZeroWritesRemaining, cmOrPdbAssetInfo.completionCode, string.Format("{0}: Command returns Completion Code {1} after 256 calls", currentApi, "WriteFruZeroWritesRemaining")); allPassed &= testPassed; // Restore App.Config with Original Values if (!ConfigureAppConfig(fruWritesRemainingKeyValue, true)) { CmTestLog.Failure(string.Format("{0}: App.Config cleanup failed", currentApi)); allPassed = false; return; } // Set App.Config key "ResetMultiRecordFruWritesRemaining" to value "1" fruWritesRemainingKeyValue["ResetMultiRecordFruWritesRemaining"] = "1"; if (!ConfigureAppConfig(fruWritesRemainingKeyValue, false)) { CmTestLog.Failure(string.Format("{0}: Setting App.Config failed for KeyValue '{1},{2}'", currentApi, fruWritesRemainingKeyValue.Keys.First(), fruWritesRemainingKeyValue.Values.First())); allPassed = false; return; } // Restart CM Service if (!RestartCmService(currentApi)) { allPassed = false; return; } if (verifyingSetCmAssetInfo) cmOrPdbAssetInfo = this.TestChannelContext.SetChassisManagerAssetInfo(string.Empty); else cmOrPdbAssetInfo = this.TestChannelContext.SetPdbAssetInfo(string.Empty); allPassed &= ChassisManagerTestHelper.AreEqual(CompletionCode.Success, cmOrPdbAssetInfo.completionCode, string.Format("{0}: FRU Writes Remaining reset", currentApi)); // Restore App.Config with Original Values if (!ConfigureAppConfig(fruWritesRemainingKeyValue, true)) { CmTestLog.Failure(string.Format("{0}: App.Config cleanup failed", currentApi)); allPassed = false; } // Restart CM Service if (!RestartCmService(currentApi)) { allPassed = false; } return; }