Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make base_serial optional in LSHW parsing #517

Merged
merged 2 commits into from
Mar 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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