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

Add gpu support #537

Merged
merged 14 commits into from
May 31, 2017
8 changes: 7 additions & 1 deletion app/collins/models/AssetMeta.scala
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,15 @@ object AssetMeta extends Schema with AnormAdapter[AssetMeta] with AssetMetaKeys
val BaseProduct = findOrCreateFromName("BASE_PRODUCT")
val BaseVendor = findOrCreateFromName("BASE_VENDOR")
val BaseSerial = findOrCreateFromName("BASE_SERIAL")
val GpuCount = findOrCreateFromName("GPU_COUNT")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be computed on reconstruction, instead of storing this? It would make it easier to compute how many NVIDIA vs AMD GPUs there are (if in a mixed env)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, I removed the extraneous GPU_COUNT

val GpuDescription = findOrCreateFromName("GPU_DESCRIPTION")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should have both vendor and description? I believe pci enumeration has fields for both vendor and description?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added product and vendor. It's a bit confusing because lshw refers to the "label" or "description" of the PCI device as the "product".


def getValues(): Seq[AssetMeta] = {
Seq(BaseDescription, BaseProduct, BaseVendor, BaseSerial)
Seq(BaseDescription, BaseProduct, BaseVendor, BaseSerial, GpuCount, GpuDescription)
}

def getLshwValues(): Set[AssetMeta] = {
Set(BaseDescription, BaseProduct, BaseVendor, BaseSerial, GpuCount, GpuDescription)
}
}
}
45 changes: 43 additions & 2 deletions app/collins/models/LshwHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package collins.models
import collins.models.AssetMeta.DynamicEnum._
import collins.models.AssetMeta.Enum._
import collins.models.lshw.Cpu
import collins.models.lshw.Gpu
import collins.models.lshw.Disk
import collins.models.lshw.Memory
import collins.models.lshw.Nic
Expand Down Expand Up @@ -34,8 +35,11 @@ object LshwHelper extends CommonHelper[LshwRepresentation] {
DiskStorageTotal
)

override val managedDynamicTags = AssetMeta.DynamicEnum.getLshwValues()

def construct(asset: Asset, lshw: LshwRepresentation): Seq[AssetMetaValue] = {
collectCpus(asset, lshw) ++
collectGpus(asset, lshw) ++
collectMemory(asset, lshw) ++
collectNics(asset, lshw) ++
collectDisks(asset, lshw) ++
Expand All @@ -45,11 +49,12 @@ object LshwHelper extends CommonHelper[LshwRepresentation] {
def reconstruct(asset: Asset, assetMeta: Seq[MetaWrapper]): Reconstruction = {
val metaMap = assetMeta.groupBy { _.getGroupId }
val (cpus,postCpuMap) = reconstructCpu(metaMap)
val (memory,postMemoryMap) = reconstructMemory(postCpuMap)
val (gpus,postGpuMap) = reconstructGpu(postCpuMap)
val (memory,postMemoryMap) = reconstructMemory(postGpuMap)
val (nics,postNicMap) = reconstructNics(postMemoryMap)
val (disks,postDiskMap) = reconstructDisks(postNicMap)
val (base,postBaseMap) = reconstructBase(postDiskMap)
(LshwRepresentation(cpus, memory, nics, disks, base.headOption.getOrElse(ServerBase())), postBaseMap.values.flatten.toSeq)
(LshwRepresentation(cpus, gpus, memory, nics, disks, base.headOption.getOrElse(ServerBase())), postBaseMap.values.flatten.toSeq)
}

protected def reconstructCpu(meta: Map[Int, Seq[MetaWrapper]]): FilteredSeq[Cpu] = {
Expand All @@ -72,6 +77,7 @@ object LshwHelper extends CommonHelper[LshwRepresentation] {
}
(cpuSeq, filteredMeta)
}

protected def collectCpus(asset: Asset, lshw: LshwRepresentation): Seq[AssetMetaValue] = {
if (lshw.cpuCount < 1) {
return Seq()
Expand All @@ -86,6 +92,41 @@ object LshwHelper extends CommonHelper[LshwRepresentation] {
)
}

protected def reconstructGpu(meta: Map[Int, Seq[MetaWrapper]]): FilteredSeq[Gpu] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i assume this is copy+paste from another reconstruct function? its really hard to read :/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I modeled reconstructGpu and collectGpus off of the existing CPU/memory/disk/NIC functions. I wasn't sure how consistent you'd want the new GPU code to be compared to the previous CPU/memory/disk/NIC stuff.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ideally this would be made more legible to make support burden easier going forward

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do, I didn't change this with my most recent commit (d019c27) but I'll get this cleaned up.

val gpuSeq = meta.foldLeft(Seq[Gpu]()) { case (seq, map) =>
val groupId = map._1
val wrapSeq = map._2
val descr = amfinder(wrapSeq, GpuDescription, _.toString, "")
if (descr.isEmpty) {
seq
} else {
Gpu(descr, "", "") +: seq
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these last 2 params ""?

}
}
val filteredMeta = meta.map { case(groupId, metaSeq) =>
val newSeq = filterNot(
metaSeq,
Set(GpuDescription.id)
)
groupId -> newSeq
}
(gpuSeq, filteredMeta)
}

protected def collectGpus(asset: Asset, lshw: LshwRepresentation): Seq[AssetMetaValue] = {
if (lshw.gpuCount < 1) {
return Seq()
}
lshw.gpus.foldLeft((0,Seq[AssetMetaValue]())) { case (run,gpu) =>
val groupId = run._1
val total = run._2
val res: Seq[AssetMetaValue] = Seq(
AssetMetaValue(asset, GpuDescription.id, groupId, "%s - %s".format(gpu.product, gpu.vendor))
)
(groupId + 1, total ++ res)
}._2
}

protected def reconstructMemory(meta: Map[Int, Seq[MetaWrapper]]): FilteredSeq[Memory] = {
if (!meta.contains(0)) {
return (Seq[Memory](), meta)
Expand Down
27 changes: 27 additions & 0 deletions app/collins/models/lshw/Gpu.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package collins.models.lshw

import play.api.libs.json.Format
import play.api.libs.json.JsObject
import play.api.libs.json.JsSuccess
import play.api.libs.json.JsValue
import play.api.libs.json.Json

object Gpu {

implicit object GpuFormat extends Format[Gpu] {
override def reads(json: JsValue) = JsSuccess(Gpu(
(json \ "DESCRIPTION").as[String],
(json \ "PRODUCT").as[String],
(json \ "VENDOR").as[String]))
override def writes(gpu: Gpu) = JsObject(Seq(
"DESCRIPTION" -> Json.toJson(gpu.description),
"PRODUCT" -> Json.toJson(gpu.product),
"VENDOR" -> Json.toJson(gpu.vendor)))
}
}

case class Gpu(
description: String, product: String, vendor: String) extends LshwAsset {
import Gpu._
override def toJsValue() = Json.toJson(this)
}
3 changes: 2 additions & 1 deletion app/collins/models/shared/CommonHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ trait CommonHelper[T] {
type FilteredSeq[T1] = Tuple2[Seq[T1], Map[Int, Seq[MetaWrapper]]]

val managedTags: Set[AssetMeta.Enum]
val managedDynamicTags: Set[AssetMeta] = Set()

/**
* Construct an appropriate AssetMetaValue sequence from the representation
Expand All @@ -27,7 +28,7 @@ trait CommonHelper[T] {
def updateAsset(asset: Asset, rep: T): Boolean = {
val mvs = construct(asset, rep)
if (!managedTags.isEmpty) {
AssetMetaValue.deleteByAssetAndMetaId(asset, managedTags.map(_.id.toLong))
AssetMetaValue.deleteByAssetAndMetaId(asset, ((managedTags.map(_.id.toLong) ++ managedDynamicTags.map(_.id))))
}

mvs.size == AssetMetaValue.create(mvs)
Expand Down
11 changes: 10 additions & 1 deletion app/collins/util/LshwRepresentation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import play.api.libs.json.Json

import collins.models.lshw.Cpu
import collins.models.lshw.Cpu.CpuFormat
import collins.models.lshw.Gpu
import collins.models.lshw.Gpu.GpuFormat
import collins.models.lshw.Disk
import collins.models.lshw.Disk.DiskFormat
import collins.models.lshw.Memory
Expand All @@ -19,23 +21,26 @@ import collins.models.lshw.ServerBase.ServerbaseFormat

object LshwRepresentation {
def empty(): LshwRepresentation = {
new LshwRepresentation(Seq(), Seq(), Seq(), Seq(), new ServerBase)
new LshwRepresentation(Seq(), Seq(), Seq(), Seq(), Seq(), new ServerBase)
}
implicit object LshwFormat extends Format[LshwRepresentation] {
import Cpu._
import Gpu._
import Disk._
import Memory._
import Nic._
import ServerBase._
import Json.toJson
override def reads(json: JsValue) = JsSuccess(LshwRepresentation(
(json \ "CPU").as[Seq[Cpu]],
(json \ "GPU").as[Seq[Gpu]],
(json \ "MEMORY").as[Seq[Memory]],
(json \ "NIC").as[Seq[Nic]],
(json \ "DISK").as[Seq[Disk]],
(json \ "BASE").as[ServerBase]))
override def writes(lshw: LshwRepresentation) = JsObject(Seq(
"CPU" -> Json.toJson(lshw.cpus),
"GPU" -> Json.toJson(lshw.gpus),
"MEMORY" -> Json.toJson(lshw.memory),
"NIC" -> Json.toJson(lshw.nics),
"DISK" -> Json.toJson(lshw.disks),
Expand All @@ -45,6 +50,7 @@ object LshwRepresentation {

case class LshwRepresentation(
cpus: Seq[Cpu],
gpus: Seq[Gpu],
memory: Seq[Memory],
nics: Seq[Nic],
disks: Seq[Disk],
Expand All @@ -61,6 +67,8 @@ case class LshwRepresentation(
}
def cpuSpeed: Double = cpus.sortBy(_.speedGhz).lastOption.map(_.speedGhz).getOrElse(0.0)

def gpuCount: Int = gpus.size

def totalMemory: ByteStorageUnit = memory.foldLeft(new ByteStorageUnit(0)) {
case (total, mem) =>
new ByteStorageUnit(total.bytes + mem.size.bytes)
Expand Down Expand Up @@ -105,6 +113,7 @@ case class LshwRepresentation(
(cpuCoreCount == other.cpuCoreCount) &&
(cpuThreadCount == other.cpuThreadCount) &&
(cpuSpeed == other.cpuSpeed) &&
(gpuCount == other.gpuCount) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am not sure where this method gets used, but comparing GPU counts (as opposed to deep comparison of desc/vendor/product) could produce odd behavior when you change GPUs and update LSHW, but the same number of GPUs are present. Would collins not update the meta attributes in the DB if the old reconstructed LSHW equals the newly submitted one?

I think what you have in this diff is fine, but we should test this behavior (induct with GPU A, change to GPU B, induct, ensure LSHW is updated properly) and open an issue if this is a problem

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, this was something I intended to remove but missed.

(totalMemory.inBytes == other.totalMemory.inBytes) &&
(memoryBanksUsed == other.memoryBanksUsed) &&
(memoryBanksUnused == other.memoryBanksUnused) &&
Expand Down
12 changes: 12 additions & 0 deletions app/collins/util/config/GpuConfig.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package collins.util.config

object GpuConfig extends Configurable {
override val namespace = "gpu"
override val referenceConfigFilename = "gpu_reference.conf"

def gpuVendors= getStringSet("gpuVendors")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like the default values to be reflected here, instead of relying on the config to include something. I.e. the default unpopulated config should include nvidia, instead of the user being forced to set this config on every installation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added default value of "NVIDIA Corporation".


override protected def validateConfig() {
gpuVendors
}
}
21 changes: 16 additions & 5 deletions app/collins/util/parsers/LshwParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package collins.util.parsers

import collins.models.lshw.LshwAsset
import collins.models.lshw.Cpu
import collins.models.lshw.Gpu
import collins.models.lshw.Memory
import collins.models.lshw.Disk
import collins.models.lshw.Nic
import collins.models.lshw.ServerBase

import collins.util.config.LshwConfig
import collins.util.config.GpuConfig
import collins.util.LshwRepresentation
import collins.util.ByteStorageUnit
import collins.util.BitStorageUnit
Expand All @@ -25,10 +27,11 @@ class LshwParser(txt: String) extends CommonParser[LshwRepresentation](txt) {

val wildcard: PartialFunction[NodeSeq, LshwAsset] = { case _ => null }
lazy val matcher = cpuMatcher.orElse(
memMatcher.orElse(
diskMatcher.orElse(
nicMatcher.orElse(
wildcard))))
gpuMatcher.orElse(
memMatcher.orElse(
diskMatcher.orElse(
nicMatcher.orElse(
wildcard)))))

override def parse(): Either[Throwable, LshwRepresentation] = {
val xml = try {
Expand All @@ -40,10 +43,11 @@ class LshwParser(txt: String) extends CommonParser[LshwRepresentation](txt) {
}
val rep = try {
val base = getBaseInfo(xml)
getCoreNodes(xml).foldLeft(LshwRepresentation(Nil, Nil, Nil, Nil, base)) {
getCoreNodes(xml).foldLeft(LshwRepresentation(Nil, Nil, Nil, Nil, Nil, base)) {
case (holder, node) =>
matcher(node) match {
case c: Cpu => holder.copy(cpus = c +: holder.cpus)
case g: Gpu => holder.copy(gpus = g +: holder.gpus)
case m: Memory => holder.copy(memory = m.copy(bank = holder.memory.size) +: holder.memory)
case d: Disk => holder.copy(disks = d +: holder.disks)
case n: Nic => holder.copy(nics = n +: holder.nics)
Expand Down Expand Up @@ -79,6 +83,13 @@ class LshwParser(txt: String) extends CommonParser[LshwRepresentation](txt) {
Cpu(cores, threads, speed, asset.description, asset.product, asset.vendor)
}

val gpuMatcher: PartialFunction[NodeSeq, Gpu] = {
case n if ((n \ "@class" text) == "display" && GpuConfig.gpuVendors.exists(s => (n \\ "vendor" text).contains(s))) => {
val asset = getAsset(n)
Gpu(asset.description, asset.product, asset.vendor)
}
}

val memMatcher: PartialFunction[NodeSeq, Memory] = {
case n if (n \ "@class" text) == "memory" && (n \ "@id" text).contains("bank:") =>
val asset = getAsset(n)
Expand Down
17 changes: 17 additions & 0 deletions app/views/asset/show_hwdetails.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@ <h4>CPU <small>Collected CPU Information</small></h4>
</tbody>
</table>

<h4>GPU <small>Collected GPU Information</small></h4>
<table class="table table-bordered table-hover table-condensed">
<thead>
<tr>
<th>Id</th><th>Description</th>
</tr>
</thead>
<tbody>
@aa.lshw.gpus.zipWithIndex.map { case(gpu,id) =>
<tr>
<th>@id</th>
<td>@gpu.description</td>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add vendor column here, and maybe sort by id, so the GPUs show up in enumeration order?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added Description (internally called "product") and Vendor columns.

</tr>
}
</tbody>
</table>

<h4>Memory <small>Collected Memory Information</small></h4>
<table class="table table-bordered table-hover table-condensed">
<thead>
Expand Down
8 changes: 8 additions & 0 deletions app/views/asset/show_overview.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ <h3>Hardware Summary <small>Summary of system components reported by LSHW</small
<td>@{if (aa.lshw.hasHyperthreadingEnabled) "Yes" else "No"}</td>
</tr>

<th colspan="3">GPU</th>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace is off here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, good catch.

</tr>
<tr>
<td></td>
<td>Total GPUs</td>
<td>@aa.lshw.gpuCount</td>
</tr>

<tr>
<th colspan="3">Memory</th>
</tr>
Expand Down
1 change: 1 addition & 0 deletions conf/docker/validations.conf
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ config.validations = [
collins.util.config.CryptoConfig,
collins.util.config.IpmiConfig,
collins.util.config.LshwConfig,
collins.util.config.GpuConfig,
collins.util.config.LldpConfig,
collins.util.config.NodeclassifierConfig,
collins.util.power.PowerConfiguration,
Expand Down
11 changes: 11 additions & 0 deletions conf/evolutions/collins/14.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# --- Add GPU information to asset_meta

# --- !Ups

INSERT INTO asset_meta (name, priority, label, description) VALUES ('GPU_COUNT', -1, 'GPU Count', 'Number of physical GPUs in asset');
INSERT INTO asset_meta (name, priority, label, description) VALUES ('GPU_DESCRIPTION', -1, 'GPU Description', 'GPU description, vendor labels');

# --- !Downs

DELETE FROM asset_meta WHERE name ='GPU_COUNT'
DELETE FROM asset_meta WHERE name ='GPU_DESCRIPTION'
5 changes: 5 additions & 0 deletions conf/reference/gpu_reference.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
gpu {
# A list of hardware vendors that should be parsed by lshw as "gpu vendors"
gpuVendors = []

}
4 changes: 4 additions & 0 deletions conf/test_base.conf
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ lshw {
lshw.defaultNicCapacity=10000000000
}

gpu {
gpuVendors = ["NVIDIA Corporation"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a stutter. gpu.gpuVendors? Maybe gpu.supportedVendorStrings or something to be more explicit

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, renamed gpuVendors -> supportedVendorStrings

}

include "authentication.conf"

# Set logging properties in logger.xml or dev_logger.xml
Expand Down
1 change: 1 addition & 0 deletions conf/validations.conf
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ config.validations = [
collins.util.config.LshwConfig,
collins.util.config.LldpConfig,
collins.util.config.NodeclassifierConfig,
collins.util.config.GpuConfig,
collins.util.power.PowerConfiguration,
collins.util.security.AuthenticationProviderConfig,
collins.util.security.FileAuthenticationProviderConfig,
Expand Down
9 changes: 9 additions & 0 deletions support/ruby/collins-client/lib/collins/asset.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,15 @@ def cpus
extract(extras, "HARDWARE", "CPU") || []
end

# @return [Fixnum] Number of GPU's found
def gpu_count
(extract(extras, "HARDWARE", "GPU") || []).length
end
# @return [Array<Hash>] GPU information
def gpus
extract(extras, "HARDWARE", "GPU") || []
end

# @return [Array<Hash>] Disk information
def disks
extract(extras, "HARDWARE", "DISK") || []
Expand Down
8 changes: 8 additions & 0 deletions support/ruby/collins-client/spec/collins/asset_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,14 @@
end
end

context "assets with GPUs should have" do
subject { Collins::Asset.from_json(CollinsFixture.full_asset_with_gpu(true)) }

it "#gpu_count" do
subject.gpu_count.should == 2
end
end

context "Update" do
it "lshw is not an attribute" do
["lshw","LSHW"].each do |name|
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"status":"success:ok","data":{"ASSET":{"ID":2,"TAG":"U000006","STATE":{"ID":1,"STATUS":null,"NAME":"NEW","LABEL":"New","DESCRIPTION":"A service in this state is inactive. It does minimal work and consumes minimal resources."},"STATUS":"New","TYPE":"SERVER_NODE","CREATED":"2017-04-26T16:52:36","UPDATED":"2017-04-26T16:52:38","DELETED":null},"HARDWARE":{"CPU":[{"CORES":6,"THREADS":6,"SPEED_GHZ":2.3,"DESCRIPTION":"AMD Opteron(tm) Processor 4174 HE Hynix Semiconductor (Hyundai Electronics)","PRODUCT":"","VENDOR":""},{"CORES":6,"THREADS":6,"SPEED_GHZ":2.3,"DESCRIPTION":"AMD Opteron(tm) Processor 4174 HE Hynix Semiconductor (Hyundai Electronics)","PRODUCT":"","VENDOR":""}],"GPU":[{"DESCRIPTION":"GM200GL [Quadro M6000] - NVIDIA Corporation","PRODUCT":"","VENDOR":""},{"DESCRIPTION":"GM200GL [Quadro M6000] - NVIDIA Corporation","PRODUCT":"","VENDOR":""}],"MEMORY":[{"SIZE":0,"SIZE_S":"0","SIZE_HUMAN":"0 Bytes","BANK":0,"DESCRIPTION":"Empty Memory Bank","PRODUCT":"","VENDOR":""},{"SIZE":0,"SIZE_S":"0","SIZE_HUMAN":"0 Bytes","BANK":1,"DESCRIPTION":"Empty Memory Bank","PRODUCT":"","VENDOR":""},{"SIZE":8589934592,"SIZE_S":"8589934592","SIZE_HUMAN":"8.00 GB","BANK":2,"DESCRIPTION":"DIMM DDR3 Synchronous 1333 MHz (0.8 ns) - Hyundai HMT31GR7BFR4A-H9","PRODUCT":"","VENDOR":""},{"SIZE":0,"SIZE_S":"0","SIZE_HUMAN":"0 Bytes","BANK":3,"DESCRIPTION":"Empty Memory Bank","PRODUCT":"","VENDOR":""},{"SIZE":0,"SIZE_S":"0","SIZE_HUMAN":"0 Bytes","BANK":4,"DESCRIPTION":"Empty Memory Bank","PRODUCT":"","VENDOR":""},{"SIZE":8589934592,"SIZE_S":"8589934592","SIZE_HUMAN":"8.00 GB","BANK":5,"DESCRIPTION":"DIMM DDR3 Synchronous 1333 MHz (0.8 ns) - Hyundai HMT31GR7BFR4A-H9","PRODUCT":"","VENDOR":""},{"SIZE":0,"SIZE_S":"0","SIZE_HUMAN":"0 Bytes","BANK":6,"DESCRIPTION":"Empty Memory Bank","PRODUCT":"","VENDOR":""},{"SIZE":0,"SIZE_S":"0","SIZE_HUMAN":"0 Bytes","BANK":7,"DESCRIPTION":"Empty Memory Bank","PRODUCT":"","VENDOR":""},{"SIZE":8589934592,"SIZE_S":"8589934592","SIZE_HUMAN":"8.00 GB","BANK":8,"DESCRIPTION":"DIMM DDR3 Synchronous 1333 MHz (0.8 ns) - Hyundai HMT31GR7BFR4A-H9","PRODUCT":"","VENDOR":""},{"SIZE":0,"SIZE_S":"0","SIZE_HUMAN":"0 Bytes","BANK":9,"DESCRIPTION":"Empty Memory Bank","PRODUCT":"","VENDOR":""},{"SIZE":0,"SIZE_S":"0","SIZE_HUMAN":"0 Bytes","BANK":10,"DESCRIPTION":"Empty Memory Bank","PRODUCT":"","VENDOR":""},{"SIZE":8589934592,"SIZE_S":"8589934592","SIZE_HUMAN":"8.00 GB","BANK":11,"DESCRIPTION":"DIMM DDR3 Synchronous 1333 MHz (0.8 ns) - Hyundai HMT31GR7BFR4A-H9","PRODUCT":"","VENDOR":""}],"NIC":[],"DISK":[],"BASE":{"DESCRIPTION":"Rack Mount Chassis","PRODUCT":"PowerEdge C6105 (N/A)","VENDOR":"Winbond Electronics","SERIAL":"FZ22YQ1"}},"LLDP":{"INTERFACES":[{"NAME":"eth0","CHASSIS":{"NAME":"accessB07.corp.uhq.ua.tc","ID":{"TYPE":"mac","VALUE":"ec:3e:f7:1e:77:c0"},"DESCRIPTION":"Juniper Networks, Inc. ex4300-48p Ethernet Switch, kernel JUNOS 14.1X53-D30.3, Build date: 2015-10-02 12:40:24 UTC Copyright (c) 1996-2015 Juniper Networks, Inc."},"PORT":{"ID":{"TYPE":"local","VALUE":"529"},"DESCRIPTION":"ge-0/0/12"},"VLANS":[{"ID":40,"NAME":"vlan-40"}]}]},"IPMI":{"ASSET_ID":2,"ASSET_TAG":"U000006","IPMI_USERNAME":"root","IPMI_PASSWORD":"YCM0PCt6y37uJsHb","IPMI_GATEWAY":"172.16.32.1","IPMI_ADDRESS":"172.16.32.20","IPMI_NETMASK":"255.255.240.0","ID":2},"ADDRESSES":[],"POWER":[],"ATTRIBS":{"0":{"CHASSIS_TAG":"U000006"}}}}
Loading