Skip to content

Commit

Permalink
Merge pull request #517 from byxorna/gabe-base-serial-optional
Browse files Browse the repository at this point in the history
Make base_serial optional in LSHW parsing
  • Loading branch information
byxorna authored Mar 13, 2017
2 parents e8b22db + c760f65 commit 347b156
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 17 deletions.
13 changes: 8 additions & 5 deletions app/collins/models/LshwHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,14 @@ object LshwHelper extends CommonHelper[LshwRepresentation] {
val baseDescription = amfinder(seq, BaseDescription, _.toString, "")
val baseProduct = amfinder(seq, BaseProduct, _.toString, "")
val baseVendor = amfinder(seq, BaseVendor, _.toString, "")
val baseSerial = amfinder(seq, BaseSerial, _.toString, "")
val baseSerial = amfinder(seq, BaseSerial, x => if (x.isEmpty) { None } else { Some(x) }, None)
Seq(ServerBase(baseDescription, baseProduct, baseVendor, baseSerial))
}.getOrElse(Nil)

val filteredMeta = meta.map { case(groupId, metaSeq) =>
val newSeq = filterNot(
metaSeq,
Set(BaseDescription.id, BaseProduct.id, BaseVendor.id)
Set(BaseDescription.id, BaseProduct.id, BaseVendor.id, BaseSerial.id)
)
groupId -> newSeq
}
Expand All @@ -229,12 +229,15 @@ object LshwHelper extends CommonHelper[LshwRepresentation] {

protected def collectBase(asset: Asset, lshw: LshwRepresentation): Seq[AssetMetaValue] = {
val base = lshw.base
Seq(
val expectedAttrs = Seq(
AssetMetaValue(asset, BaseDescription.id, base.description),
AssetMetaValue(asset, BaseProduct.id, base.product),
AssetMetaValue(asset, BaseVendor.id, base.vendor),
AssetMetaValue(asset, BaseSerial.id, base.serial)
AssetMetaValue(asset, BaseVendor.id, base.vendor)
)
base.serial match {
case Some(x) => expectedAttrs ++ Seq(AssetMetaValue(asset, BaseSerial.id, base.serial.get))
case None => expectedAttrs
}
}

}
4 changes: 2 additions & 2 deletions app/collins/models/lshw/ServerBase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ object ServerBase {
(json \ "DESCRIPTION").as[String],
(json \ "PRODUCT").as[String],
(json \ "VENDOR").as[String],
(json \ "SERIAL").as[String]))
(json \ "SERIAL").asOpt[String]))
override def writes(serverbase: ServerBase) = JsObject(Seq(
"DESCRIPTION" -> Json.toJson(serverbase.description),
"PRODUCT" -> Json.toJson(serverbase.product),
Expand All @@ -22,7 +22,7 @@ object ServerBase {
}

case class ServerBase(
description: String = "", product: String = "", vendor: String = "", serial: String = "") extends LshwAsset {
description: String = "", product: String = "", vendor: String = "", serial: Option[String] = None) extends LshwAsset {
import ServerBase._
override def toJsValue() = Json.toJson(this)

Expand Down
1 change: 1 addition & 0 deletions app/collins/util/parsers/LldpParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class LldpParser(txt: String) extends CommonParser[LldpRepresentation](txt) {
}

protected def findVlans(seq: NodeSeq): Seq[Vlan] = {
// TODO(gabe): make this less brittle and handle missing vlan-id
(seq \\ "vlan").foldLeft(Seq[Vlan]()) {
case (vseq, vlan) =>
val id = Option(vlan \ "@vlan-id" text).filter(_.nonEmpty).getOrElse("")
Expand Down
5 changes: 3 additions & 2 deletions app/collins/util/parsers/LshwParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,14 @@ class LshwParser(txt: String) extends CommonParser[LshwRepresentation](txt) {
// the correct element
if ((elem \ "@class" text).toString == "system") {
val asset = getAsset(elem)
val serial = (elem \ "serial" text)
// serial may be missing, so be flexible here and allow it to be absent
val serial = (elem \ "serial" headOption).map(_.text)
ServerBase(asset.description, asset.product, asset.vendor, serial)
} // To spice things up, sometimes we get <list>$everything</list>
// instead of just $everything
else if (((elem \ "node") \ "@class" text) == "system") {
val asset = getAsset(elem \ "node")
val serial = (elem \ "serial" text)
val serial = (elem \ "serial" headOption).map(_.text)
ServerBase(asset.description, asset.product, asset.vendor, serial)
} else {
throw MalformedAttributeException("Expected root class=system node attribute")
Expand Down
21 changes: 14 additions & 7 deletions app/views/asset/show_overview.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ <h3>Asset Overview <small>System and user attributes</small></h3>
}
@if(aa.addresses.size > 0) {
<tr>
<th>Ip Addresses</th>
<th><strong>IP Addresses</strong></th>
<td>@TagDecorator.decorate("IP_ADDRESS", aa.addresses.map(_.dottedAddress).toList, ", ")</td>
<td>Primary IP Addresses</td>
</tr>
}
<tr>
<th>Asset Tag</th>
<th>
<strong data-rel="tooltip" title="" data-original-title="TAG">Asset Tag</strong>
</th>
@if(SoftLayerConfig.enabled && SoftLayer.isSoftLayerAsset(aa.asset)) {
@slLink(aa.asset, aa.asset.tag)
} else {
Expand All @@ -40,7 +42,7 @@ <h3>Asset Overview <small>System and user attributes</small></h3>
</tr>
<tr>
@defining(aa.asset.nodeClass){ nodeclass =>
<th>Classification</th>
<th><strong>Classification</strong> <a href="https://tumblr.github.io/collins/configuration.html#node classifier"><i class="glyphicon glyphicon-question-sign" data-rel="tooltip" title="" data-original-title="Asset classification according to node classifier"></i></a></th>
<td>
<span>
@nodeclass.map { nc =>
Expand All @@ -60,12 +62,12 @@ <h3>Asset Overview <small>System and user attributes</small></h3>
}
</tr>
<tr>
<th>Asset Type</th>
<th><strong>Asset Type<strong> <a href="https://tumblr.github.io/collins/concepts.html#assets"><i class="glyphicon glyphicon-question-sign"></i></a></th>
<td>@aa.asset.assetType.label</td>
<td></td>
</tr>
<tr class="@statusClassFromAsset(aa.asset)">
<td><strong>Asset Status</strong></td>
<td><strong>Asset Status</strong> <a href="https://tumblr.github.io/collins/concepts.html#status & state"><i class="glyphicon glyphicon-question-sign"></i></a></td>
<td>@aa.asset.getStatusName</td>
@if(aa.asset.isMaintenance) {
<td><a href="#user-log-section"><span class="label label-primary">See Notes</span></a> @aa.asset.status.description</td>
Expand All @@ -76,7 +78,7 @@ <h3>Asset Overview <small>System and user attributes</small></h3>
@if(aa.asset.stateId != 0) {
@State.findById(aa.asset.stateId).map { state =>
<tr>
<th>Asset State</th>
<th><strong>Asset State</strong> <a href="https://tumblr.github.io/collins/concepts.html#status & state"><i class="glyphicon glyphicon-question-sign"></i></a></th>
<td>@state.label</td>
<td>@state.description</td>
</tr>
Expand All @@ -94,7 +96,12 @@ <h3>Asset Overview <small>System and user attributes</small></h3>
</tr>
@MetaValueOrderer.order(aa.mvs.filter(_.getName() != "HOSTNAME")).map { case(size, mv) =>
<tr>
<th>@mv.getLabel() @if(size > 1 || mv.getGroupId() != 0){(@mv.getGroupId())}</th>
<th>
<span @if(mv.getLabel().toUpperCase != mv.getName()){
data-rel="tooltip" title="" data-original-title="@mv.getName() @if(size > 1 || mv.getGroupId() != 0){(@mv.getGroupId())}"
}>@mv.getLabel() @if(size > 1 || mv.getGroupId() != 0){(@mv.getGroupId())}
</span>
</th>
<td>
@{
mv.getName match {
Expand Down
15 changes: 14 additions & 1 deletion test/collins/util/parsers/LshwParserSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class LshwParserSpec extends mutable.Specification {

rep.base.product mustEqual "PowerEdge C6105 (N/A)"
rep.base.vendor mustEqual "Winbond Electronics"
rep.base.serial mustEqual "FZ1NXQ1"
rep.base.serial.get mustEqual "FZ1NXQ1"
}
} // with a 10-gig card

Expand Down Expand Up @@ -386,6 +386,19 @@ class LshwParserSpec extends mutable.Specification {

} // Parse softlayer supermicro (Intel) lshw output"

"Handle missing fields in LSHW" in {
"No base serial" in new LshwParserHelper("missing-base-serial.xml") {
val parseResults = parsed()
parseResults must beRight
parseResults.right.toOption must beSome.which { rep =>
rep.base.product mustEqual "Virtual Machine (None)"
rep.base.vendor mustEqual "Microsoft Corporation"
rep.base.serial mustEqual None
rep.base.description mustEqual "Desktop Computer"
}
}
}

"Parse Dell LSHW Output" in {
"R620 LSHW Output" in new LshwParserHelper("lshw-dell-r620-single-cpu.xml") {
val parseResults = parsed()
Expand Down
Loading

0 comments on commit 347b156

Please sign in to comment.