CycloneTechnology/ChaMP

View on GitHub
champ-ipmi/src/main/scala/com/cyclone/ipmi/command/ipmiMessagingSupport/GetSystemInfoParameters.scala

Summary

Maintainability
A
0 mins
Test Coverage
package com.cyclone.ipmi.command.ipmiMessagingSupport

import akka.util.{ByteString, ByteStringBuilder}
import com.cyclone.ipmi.StatusCodeError
import com.cyclone.ipmi.codec._
import com.cyclone.ipmi.command.{CommandCode, NetworkFunction, StatusCode, StatusCodeTranslator}
import com.cyclone.ipmi.protocol.packet.{CommandResultCodec, IpmiCommandResult, IpmiStandardCommand}

case class ParameterRevision(present: Int, required: Int)

object ParameterRevision {
  implicit val decoder: Decoder[ParameterRevision] = new Decoder[ParameterRevision] {

    def decode(data: ByteString) =
      ParameterRevision(
        present = data(0).bits4To7.toUnsignedInt,
        required = data(0).bits0To3.toUnsignedInt
      )
  }
}

/**
  * Get System Info Parameters command and response
  */
object GetSystemInfoParameters {

  case object ParameterNotSupported extends StatusCodeError {
    val code = StatusCode(0x80.toByte)
    val message = "Parameter not supported"
  }

  trait ParameterSelector {
    def code: Int
  }

  object ParameterSelector {
    implicit def coder[P <: ParameterSelector]: Coder[P] = new Coder[P] {
      def encode(a: P) = ByteString(a.code)
    }

    case object SetInProgress extends ParameterSelector {
      val code = 0x00

      sealed trait SetState

      object SetState {

        case object Complete extends SetState

        case object InProgress extends SetState

        case object CommitWrite extends SetState

        case object Reserved extends SetState

      }

      case class Data(setState: SetState)

      object Data {
        implicit val decoder: Decoder[Data] = new Decoder[Data] {

          def decode(data: ByteString): Data = {
            val setState = data(0).toUnsignedInt match {
              case 0 => SetState.Complete
              case 1 => SetState.InProgress
              case 2 => SetState.CommitWrite
              case 3 => SetState.Reserved
            }

            Data(setState)
          }
        }
      }

      implicit val parameterCodec: ParameterCodec[SetInProgress.type, Data, Nothing] =
        ParameterCodec.parameterCodecFor[SetInProgress.type, SetInProgress.Data]
    }

    case class BlockData(setSelector: Int, data: ByteString)

    object BlockData {
      implicit val decoder: Decoder[BlockData] = new Decoder[BlockData] {

        def decode(data: ByteString) =
          BlockData(data(0), data.drop(1))
      }
    }

    case class LongString(value: String)

    object LongString {
      implicit val blockDecoder: BlockDecoder[LongString] = new BlockDecoder[LongString] {

        def decode(encoding: StringDecoder, data: ByteString) =
          LongString(data.as(encoding))
      }
    }

    case class ShortString(value: String)

    object ShortString {
      implicit val decoder: Decoder[ShortString] = new Decoder[ShortString] {

        def decode(data: ByteString): ShortString = {
          val len = data(0).toUnsignedInt
          ShortString(data.drop(1).take(len).as(StringDecoder.AsciiLatin))
        }
      }
    }

    case object SystemFirmwareVersion extends ParameterSelector {
      val code = 0x01

      implicit val parameterCodec: ParameterCodec[SystemFirmwareVersion.type, BlockData, LongString] =
        ParameterCodec.blockParameterCodecFor[SystemFirmwareVersion.type, BlockData, LongString]
    }

    case object SystemName extends ParameterSelector {
      val code = 0x02

      implicit val parameterCodec: ParameterCodec[SystemName.type, BlockData, LongString] =
        ParameterCodec.blockParameterCodecFor[SystemName.type, BlockData, LongString]
    }

    case object PrimaryOperatingSystemName extends ParameterSelector {
      val code = 0x03

      implicit val parameterCodec: ParameterCodec[PrimaryOperatingSystemName.type, BlockData, LongString] =
        ParameterCodec
          .blockParameterCodecFor[PrimaryOperatingSystemName.type, BlockData, LongString]
    }

    case object OperatingSystemName extends ParameterSelector {
      val code = 0x04

      implicit val parameterCodec: ParameterCodec[OperatingSystemName.type, BlockData, LongString] =
        ParameterCodec.blockParameterCodecFor[OperatingSystemName.type, BlockData, LongString]
    }

    case object PresentOsVersionNumber extends ParameterSelector {
      val code = 0x05

      implicit val parameterCodec: ParameterCodec[PresentOsVersionNumber.type, BlockData, LongString] =
        ParameterCodec.blockParameterCodecFor[PresentOsVersionNumber.type, BlockData, LongString]
    }

    case object BmcUrl extends ParameterSelector {
      val code = 0x06

      implicit val parameterCodec: ParameterCodec[BmcUrl.type, BlockData, LongString] =
        ParameterCodec.blockParameterCodecFor[BmcUrl.type, BlockData, LongString]
    }

    case object BaseOsHypervisorUrlForManageability extends ParameterSelector {
      val code = 0x07

      implicit val parameterCodec: ParameterCodec[BaseOsHypervisorUrlForManageability.type, BlockData, LongString] =
        ParameterCodec
          .blockParameterCodecFor[BaseOsHypervisorUrlForManageability.type, BlockData, LongString]
    }

    // NB: OEM codes can be specified in OEM specific object modules
  }

  trait BlockDecoder[B] {
    def decode(encoding: StringDecoder, data: ByteString): B
  }

  object BlockDecoder {

    implicit object NoBlockDecoder extends BlockDecoder[Nothing] {

      def decode(encoding: StringDecoder, data: ByteString) =
        throw new UnsupportedOperationException
    }

  }

  /**
    * [[ParameterSelector]] specific response data.
    *
    * Associate selector and response types
    * by providing an implicit [[ParameterCodec]] instance inside the
    * [[ParameterSelector]]
    *
    * Some parameters will be encoded in the result of a single command.
    * Others require multiple commands to create a block of data that is decoded.
    */
  object ParameterCodec {

    def parameterCodecFor[P <: ParameterSelector: Coder, D: Decoder]: ParameterCodec[P, D, Nothing] =
      ParameterCodec[P, D, Nothing](
        implicitly[Coder[P]],
        implicitly[Decoder[D]],
        BlockDecoder.NoBlockDecoder
      )

    def blockParameterCodecFor[P <: ParameterSelector: Coder, D: Decoder, B: BlockDecoder] =
      ParameterCodec(implicitly[Coder[P]], implicitly[Decoder[D]], implicitly[BlockDecoder[B]])
  }

  case class ParameterCodec[P <: ParameterSelector, D, B](
    coder: Coder[P],
    decoder: Decoder[D],
    blockDecoder: BlockDecoder[B]
  )

  object CommandResult {
    implicit def decoder[D: Decoder]: Decoder[CommandResult[D]] = new Decoder[CommandResult[D]] {

      def decode(data: ByteString): CommandResult[D] = {
        val iterator = data.iterator
        val is = iterator.asInputStream

        val parameterRevision = is.readByte.as[ParameterRevision]

        val responseData = iterator.toByteString.as[D]

        CommandResult(parameterRevision, responseData)
      }
    }

    implicit val statusCodeTranslator: StatusCodeTranslator[CommandResult[_]] =
      StatusCodeTranslator[CommandResult[_]] {
        case ParameterNotSupported.code => ParameterNotSupported
      }
  }

  case class CommandResult[+D: Decoder](parameterRevision: ParameterRevision, data: D) extends IpmiCommandResult

  object Command {
    implicit def coder[P <: ParameterSelector: Coder]: Coder[Command[P]] = new Coder[Command[P]] {

      def encode(request: Command[P]): ByteString = {
        import request._

        val b = new ByteStringBuilder

        b += 0x00.toByte // bit7 0b = Get Parameter, 1b = Get Parameter Revision Only, bits0to6 - reserved
        b ++= parameterSelector.toBin
        b += setSelector.toByte
        b += blockSelector.toByte

        b.result()
      }
    }

    implicit def codec[P <: ParameterSelector, D](
      implicit codec: ParameterCodec[P, D, _]
    ): CommandResultCodec[Command[P], CommandResult[D]] = {
      implicit val decoder: Decoder[D] = codec.decoder
      implicit val coder: Coder[P] = codec.coder
      CommandResultCodec.commandResultCodecFor[Command[P], CommandResult[D]]
    }
  }

  case class Command[P <: ParameterSelector](
    parameterSelector: P,
    setSelector: Int = 0,
    blockSelector: Int = 0
  ) extends IpmiStandardCommand {

    val networkFunction: NetworkFunction = NetworkFunction.ApplicationRequest
    val commandCode = CommandCode(0x59)
  }

}