From e175f100247f74c0b714ba404b35258292cce050 Mon Sep 17 00:00:00 2001 From: Gabe Conradi Date: Tue, 7 Mar 2017 16:10:21 -0500 Subject: [PATCH 1/2] make lshw base_serial optional and omit dupes from overview drop duplicate BASE_SERIAL attrs add some docs and tooltips to attributes in show_overview fix some tooltips --- app/collins/models/LshwHelper.scala | 13 ++++++++----- app/collins/models/lshw/ServerBase.scala | 4 ++-- app/collins/util/parsers/LldpParser.scala | 1 + app/collins/util/parsers/LshwParser.scala | 5 +++-- app/views/asset/show_overview.scala.html | 21 ++++++++++++++------- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/app/collins/models/LshwHelper.scala b/app/collins/models/LshwHelper.scala index 4a6f27bb2..72df84c8c 100644 --- a/app/collins/models/LshwHelper.scala +++ b/app/collins/models/LshwHelper.scala @@ -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 } @@ -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 + } } } diff --git a/app/collins/models/lshw/ServerBase.scala b/app/collins/models/lshw/ServerBase.scala index eee1378cf..0400ed353 100644 --- a/app/collins/models/lshw/ServerBase.scala +++ b/app/collins/models/lshw/ServerBase.scala @@ -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), @@ -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) diff --git a/app/collins/util/parsers/LldpParser.scala b/app/collins/util/parsers/LldpParser.scala index 433bea3dd..203ec3346 100644 --- a/app/collins/util/parsers/LldpParser.scala +++ b/app/collins/util/parsers/LldpParser.scala @@ -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("") diff --git a/app/collins/util/parsers/LshwParser.scala b/app/collins/util/parsers/LshwParser.scala index d1a12a778..97dbfa0d0 100644 --- a/app/collins/util/parsers/LshwParser.scala +++ b/app/collins/util/parsers/LshwParser.scala @@ -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 $everything // 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") diff --git a/app/views/asset/show_overview.scala.html b/app/views/asset/show_overview.scala.html index 03a9e50a5..42d611963 100644 --- a/app/views/asset/show_overview.scala.html +++ b/app/views/asset/show_overview.scala.html @@ -24,13 +24,15 @@

Asset Overview System and user attributes

} @if(aa.addresses.size > 0) { - Ip Addresses + IP Addresses @TagDecorator.decorate("IP_ADDRESS", aa.addresses.map(_.dottedAddress).toList, ", ") Primary IP Addresses } - Asset Tag + + Asset Tag + @if(SoftLayerConfig.enabled && SoftLayer.isSoftLayerAsset(aa.asset)) { @slLink(aa.asset, aa.asset.tag) } else { @@ -40,7 +42,7 @@

Asset Overview System and user attributes

@defining(aa.asset.nodeClass){ nodeclass => - Classification + Classification @nodeclass.map { nc => @@ -60,12 +62,12 @@

Asset Overview System and user attributes

} - Asset Type + Asset Type @aa.asset.assetType.label - Asset Status + Asset Status @aa.asset.getStatusName @if(aa.asset.isMaintenance) { See Notes @aa.asset.status.description @@ -76,7 +78,7 @@

Asset Overview System and user attributes

@if(aa.asset.stateId != 0) { @State.findById(aa.asset.stateId).map { state => - Asset State + Asset State @state.label @state.description @@ -94,7 +96,12 @@

Asset Overview System and user attributes

@MetaValueOrderer.order(aa.mvs.filter(_.getName() != "HOSTNAME")).map { case(size, mv) => - @mv.getLabel() @if(size > 1 || mv.getGroupId() != 0){(@mv.getGroupId())} + + @mv.getLabel() @if(size > 1 || mv.getGroupId() != 0){(@mv.getGroupId())} + + @{ mv.getName match { From c760f650fd45d73831f27d0c9e61aad5a7a7a45b Mon Sep 17 00:00:00 2001 From: Gabe Conradi Date: Thu, 9 Mar 2017 14:17:19 -0500 Subject: [PATCH 2/2] add test for missing base serial in lshw --- .../collins/util/parsers/LshwParserSpec.scala | 15 +- test/resources/missing-base-serial.xml | 259 ++++++++++++++++++ 2 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 test/resources/missing-base-serial.xml diff --git a/test/collins/util/parsers/LshwParserSpec.scala b/test/collins/util/parsers/LshwParserSpec.scala index 4c770cffd..71d59d571 100644 --- a/test/collins/util/parsers/LshwParserSpec.scala +++ b/test/collins/util/parsers/LshwParserSpec.scala @@ -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 @@ -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() diff --git a/test/resources/missing-base-serial.xml b/test/resources/missing-base-serial.xml new file mode 100644 index 000000000..18eb4e6ca --- /dev/null +++ b/test/resources/missing-base-serial.xml @@ -0,0 +1,259 @@ + + + + + + + + Desktop Computer + Virtual Machine (None) + Microsoft Corporation + Hyper-V UEFI Release v1.0 + 0574-2207-1613-4261-5019-1454-34 + 64 + + + + + + + + + SMBIOS version 2.4 + DMI version 2.4 + 32-bit processes + + + Motherboard + Virtual Machine + Microsoft Corporation + 0 + Hyper-V UEFI Release v1.0 + 0574-2207-1613-4261-5019-1454-34 + Virtual Machine + + BIOS + Microsoft Corporation + 0 + Hyper-V UEFI Release v1.0 + 11/26/2012 + 1048576 + + ACPI + + + + CPU + Xeon (None) + Intel Corp. + 4 + cpu@0 + Intel(R) Xeon(R) CPU E5-2630 v3 @ 2.40GHz + None + None + 2400000000 + 4000000000 + 64 + 3705032704 + + mathematical co-processor + FPU exceptions reporting + + virtual mode extensions + debugging extensions + page size extensions + time stamp counter + model-specific registers + 4GB+ memory addressing (Physical Address Extension) + machine check exceptions + compare and exchange 8-byte + on-chip advanced programmable interrupt controller (APIC) + fast system calls + memory type range registers + page global enable + machine check architecture + conditional move instruction + page attribute table + 36-bit page size extensions + + multimedia extensions (MMX) + fast floating point save/restore + streaming SIMD extensions (SSE) + streaming SIMD extensions (SSE2) + self-snoop + fast system calls + no-execute bit (NX) + 64bits extensions (x86-64) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + System Memory + 6 + System board or motherboard + 4294967296 + + None + Microsoft Corporation + 0 + None + M00 + 4160749568 + + + None + Microsoft Corporation + 1 + None + M01 + 134217728 + + + + 1 + scsi0 + + SCSI Disk + 0.0.0 + scsi@0:0.0.0 + /dev/sda + 8:0 + 53687091200 + + + + + + + GUID Partition Table version 1.00 + Partitioned disk + GUID partition table + + + Windows FAT volume + mkfs.fat + 1 + scsi@0:0.0.0,1 + FAT16 + 32c4-96a4 + 209714688 + 209714688 + + + + + + + Contains boot code + Windows FAT + initialized volume + + + + data partition + Windows + 2 + scsi@0:0.0.0,2 + c1747320-6774-47dc-8e61-104051115270 + 524287488 + + + LVM Physical Volume + Linux + 3 + scsi@0:0.0.0,3 + /dev/sda3 + 8:3 + tpeGWj-gIj7-Xpa7-22j6-LqZf-qP6k-ayZLO1 + 52950990848 + + Multi-volumes + + + + + + SCSI CD-ROM + 0.0.1 + scsi@0:0.0.1 + /dev/cdrom + /dev/sr0 + 11:0 + + + + + Audio CD playback + + + + + + Ethernet interface + 1 + veth7720825 + e2:89:fc:fa:6c:2c + 10000000000 + + + + + + + + + + + + + + Physical interface + + + + Ethernet interface + 2 + eth0 + 00:1d:d8:b7:1e:e7 + + + + + + + + + + + Physical interface + + + +