From a7da9798e8531b3956984e0ab5537289d31ce638 Mon Sep 17 00:00:00 2001 From: Bhaskar Maddala Date: Fri, 7 Aug 2015 09:02:53 -0400 Subject: [PATCH] Instrumenting with stats around the serialization of json. Summary: The previous change instrumented stats around the conversion of data type to json objects. This reverts that change and instrument the "stringification" of json. --- app/collins/controllers/ApiResponse.scala | 28 ++-- app/collins/models/AssetType.scala | 26 ++-- app/collins/models/State.scala | 36 ++--- app/collins/models/Status.scala | 26 ++-- app/collins/models/asset/AllAttributes.scala | 7 +- app/collins/models/asset/conversions.scala | 46 +++--- app/collins/models/conversions.scala | 153 ++++++++----------- app/collins/models/lldp/Chassis.scala | 26 ++-- app/collins/models/lldp/ChassisId.scala | 22 +-- app/collins/models/lldp/Interface.scala | 30 ++-- app/collins/models/lldp/Port.scala | 22 +-- app/collins/models/lldp/PortId.scala | 22 +-- app/collins/models/lldp/Vlan.scala | 22 +-- app/collins/models/lshw/Cpu.scala | 38 ++--- app/collins/models/lshw/Disk.scala | 37 ++--- app/collins/models/lshw/Memory.scala | 37 ++--- app/collins/models/lshw/Nic.scala | 37 ++--- app/collins/models/lshw/ServerBase.scala | 30 ++-- app/collins/util/LldpRepresentation.scala | 16 +- app/collins/util/LshwRepresentation.scala | 32 ++-- app/collins/util/power/PowerComponent.scala | 40 +++-- app/collins/util/power/PowerUnit.scala | 24 ++- 22 files changed, 318 insertions(+), 439 deletions(-) diff --git a/app/collins/controllers/ApiResponse.scala b/app/collins/controllers/ApiResponse.scala index ed8741606..de24c6c0a 100644 --- a/app/collins/controllers/ApiResponse.scala +++ b/app/collins/controllers/ApiResponse.scala @@ -1,5 +1,9 @@ package collins.controllers +import scala.concurrent.Future + +import play.api.mvc.Result + import play.api.libs.json.JsArray import play.api.libs.json.JsBoolean import play.api.libs.json.JsNull @@ -9,18 +13,20 @@ import play.api.libs.json.JsString import play.api.libs.json.JsUndefined import play.api.libs.json.JsValue import play.api.libs.json.Json + import play.api.mvc.AnyContent import play.api.mvc.Controller import play.api.mvc.Request import play.api.mvc.Results + +import collins.util.Stats import collins.util.BashOutput import collins.util.HtmlOutput import collins.util.JsonOutput import collins.util.OutputType import collins.util.OutputType.contentTypeWithCharset import collins.util.TextOutput -import scala.concurrent.Future -import play.api.mvc.Result + object ApiResponse extends ApiResponse { import OutputType.contentTypeWithCharset @@ -125,11 +131,7 @@ trait ApiResponse extends Controller { case o: BashOutput => response.status(formatBashResponse(response.data) + "\n").as(contentTypeWithCharset(o)).withHeaders(response.headers:_*) case o: JsonOutput => - val rewritten = ApiResponse.isJsonErrorMessage(response.data) match { - case true => response.data - case false => ApiResponse.formatJsonMessage(response.status, response.data) - } - response.status(Json.stringify(rewritten)).as(contentTypeWithCharset(o)).withHeaders(response.headers:_*) + response.status(formatJsonResponse(response.status, response.data)).as(contentTypeWithCharset(o)).withHeaders(response.headers:_*) case o: HtmlOutput => val e = new Exception("Unhandled view") e.printStackTrace() @@ -137,7 +139,15 @@ trait ApiResponse extends Controller { } } - protected def formatBashResponse(jsobject: JsValue, prefix: String = ""): String = { + private[this] def formatJsonResponse(status: Results.Status, jsobject: JsValue): String = Stats.time("Response.AsJson") { + val rewritten = ApiResponse.isJsonErrorMessage(jsobject) match { + case true => jsobject + case false => ApiResponse.formatJsonMessage(status, jsobject) + } + Json.stringify(rewritten) + } + + private[this] def formatBashResponse(jsobject: JsValue, prefix: String = ""): String = Stats.time("Response.AsBash") { def formatBasic(jsvalue: JsValue): String = { jsvalue match { case JsNull => "" @@ -194,7 +204,7 @@ trait ApiResponse extends Controller { }.mkString("\n") } - protected def formatTextResponse(jsobject: JsValue, depth: Int = 0): String = { + private[this] def formatTextResponse(jsobject: JsValue, depth: Int = 0): String = Stats.time("Response.AsText") { def formatBasic(jsvalue: JsValue): String = { jsvalue match { case JsNull => "null" diff --git a/app/collins/models/AssetType.scala b/app/collins/models/AssetType.scala index a0b63d9ec..de7c1a45f 100644 --- a/app/collins/models/AssetType.scala +++ b/app/collins/models/AssetType.scala @@ -9,8 +9,6 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - import collins.models.cache.Cache import collins.models.shared.AnormAdapter import collins.models.shared.ValidatedEntity @@ -35,20 +33,16 @@ object AssetType extends Schema with AnormAdapter[AssetType] with AssetTypeKeys )) implicit object AssetTypeFormat extends Format[AssetType] { - override def reads(json: JsValue) = Stats.time("AssetType.Reads") { - JsSuccess(AssetType( - (json \ "NAME").as[String], - (json \ "LABEL").as[String], - (json \ "ID").asOpt[Int].getOrElse(0) - )) - } - override def writes(at: AssetType) = Stats.time("AssetType.Writes") { - JsObject(Seq( - "ID" -> Json.toJson(at.id), - "NAME" -> Json.toJson(at.name), - "LABEL" -> Json.toJson(at.label) - )) - } + override def reads(json: JsValue) = JsSuccess(AssetType( + (json \ "NAME").as[String], + (json \ "LABEL").as[String], + (json \ "ID").asOpt[Int].getOrElse(0) + )) + override def writes(at: AssetType) = JsObject(Seq( + "ID" -> Json.toJson(at.id), + "NAME" -> Json.toJson(at.name), + "LABEL" -> Json.toJson(at.label) + )) } def findById(id: Int): Option[AssetType] = Cache.get(findByIdKey(id), inTransaction { diff --git a/app/collins/models/State.scala b/app/collins/models/State.scala index 65852cd37..b2d97aef8 100644 --- a/app/collins/models/State.scala +++ b/app/collins/models/State.scala @@ -9,12 +9,10 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - +import collins.models.cache.Cache import collins.validation.Pattern.isAlphaNumericString import collins.validation.Pattern.isNonEmptyString -import collins.models.cache.Cache import collins.models.shared.AnormAdapter import collins.models.shared.ValidatedEntity import collins.models.Status.StatusFormat @@ -36,24 +34,20 @@ object State extends Schema with AnormAdapter[State] with StateKeys { implicit object StateFormat extends Format[State] { import Status.StatusFormat - override def reads(json: JsValue) = Stats.time("State.Reads") { - JsSuccess(State( - (json \ "ID").asOpt[Int].getOrElse(0), - (json \ "STATUS").asOpt[Int].getOrElse(ANY_STATUS), - (json \ "NAME").as[String], - (json \ "LABEL").as[String], - (json \ "DESCRIPTION").as[String] - )) - } - override def writes(state: State) = Stats.time("State.Writes") { - JsObject(Seq( - "ID" -> Json.toJson(state.id), - "STATUS" -> Json.toJson(Status.findById(state.status)), - "NAME" -> Json.toJson(state.name), - "LABEL" -> Json.toJson(state.label), - "DESCRIPTION" -> Json.toJson(state.description) - )) - } + override def reads(json: JsValue) = JsSuccess(State( + (json \ "ID").asOpt[Int].getOrElse(0), + (json \ "STATUS").asOpt[Int].getOrElse(ANY_STATUS), + (json \ "NAME").as[String], + (json \ "LABEL").as[String], + (json \ "DESCRIPTION").as[String] + )) + override def writes(state: State) = JsObject(Seq( + "ID" -> Json.toJson(state.id), + "STATUS" -> Json.toJson(Status.findById(state.status)), + "NAME" -> Json.toJson(state.name), + "LABEL" -> Json.toJson(state.label), + "DESCRIPTION" -> Json.toJson(state.description) + )) } override val tableDef = table[State]("state") on(tableDef)(s => declare( diff --git a/app/collins/models/Status.scala b/app/collins/models/Status.scala index 9ee0f9ea8..b3af88347 100644 --- a/app/collins/models/Status.scala +++ b/app/collins/models/Status.scala @@ -12,8 +12,6 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - import collins.models.cache.Cache import collins.models.shared.AnormAdapter import collins.models.shared.ValidatedEntity @@ -43,20 +41,16 @@ object Status extends Schema with AnormAdapter[Status] with StatusKeys { def Unallocated = Status.findByName("Unallocated") implicit object StatusFormat extends Format[Status] { - override def reads(json: JsValue) = Stats.time("Status.Reads") { - JsSuccess(Status( - (json \ "NAME").as[String], - (json \ "DESCRIPTION").as[String], - (json \ "ID").as[Int] - )) - } - override def writes(status: Status) = Stats.time("Status.Writes") { - JsObject(Seq( - "ID" -> JsNumber(status.id), - "NAME" -> JsString(status.name), - "DESCRIPTION" -> JsString(status.description) - )) - } + override def reads(json: JsValue) = JsSuccess(Status( + (json \ "NAME").as[String], + (json \ "DESCRIPTION").as[String], + (json \ "ID").as[Int] + )) + override def writes(status: Status) = JsObject(Seq( + "ID" -> JsNumber(status.id), + "NAME" -> JsString(status.name), + "DESCRIPTION" -> JsString(status.description) + )) } override val tableDef = table[Status]("status") diff --git a/app/collins/models/asset/AllAttributes.scala b/app/collins/models/asset/AllAttributes.scala index 6990d8c08..a64d33e42 100644 --- a/app/collins/models/asset/AllAttributes.scala +++ b/app/collins/models/asset/AllAttributes.scala @@ -5,8 +5,6 @@ import play.api.libs.json.JsString import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - import collins.models.Asset import collins.models.AssetMetaValue import collins.models.IpAddresses @@ -17,7 +15,6 @@ import collins.models.MetaWrapper import collins.models.PowerHelper import collins.models.conversions.IpAddressFormat import collins.models.conversions.IpmiFormat - import collins.util.LldpRepresentation import collins.util.LshwRepresentation import collins.util.config.Feature @@ -26,7 +23,7 @@ import collins.util.power.PowerUnits import collins.util.power.PowerUnits object AllAttributes { - def get(asset: Asset): AllAttributes = Stats.time("Asset.GetAllAttributes") { + def get(asset: Asset): AllAttributes = { if (asset.isConfiguration) { AllAttributes(asset, LshwRepresentation.empty, @@ -74,7 +71,7 @@ case class AllAttributes( } } - def toJsValue(): JsValue = Stats.time("SerializeAsset.AllAttributes") { + def toJsValue(): JsValue = { val outSeq = Seq( "ASSET" -> asset.toJsValue, "HARDWARE" -> lshw.toJsValue, diff --git a/app/collins/models/asset/conversions.scala b/app/collins/models/asset/conversions.scala index b4c1c87c0..4546e4237 100644 --- a/app/collins/models/asset/conversions.scala +++ b/app/collins/models/asset/conversions.scala @@ -10,8 +10,6 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - import collins.models.Asset import collins.models.AssetType import collins.models.State @@ -22,29 +20,25 @@ import collins.models.conversions.TimestampFormat object conversions { import collins.models.State.StateFormat implicit object AssetFormat extends Format[AssetView] { - override def reads(json: JsValue) = Stats.time("AssetView.Reads") { - JsSuccess(Asset( - (json \ "TAG").as[String], - Status.findByName((json \ "STATUS").as[String]).map(_.id).get, - AssetType.findByName((json \ "TYPE").as[String]).map(_.id).get, - (json \ "CREATED").as[Timestamp], - (json \ "UPDATED").asOpt[Timestamp], - (json \ "DELETED").asOpt[Timestamp], - (json \ "ID").as[Long], - (json \ "STATE").asOpt[State].map(_.id).getOrElse(0) - )) - } - override def writes(asset: AssetView): JsObject = Stats.time("AssetView.Writes") { - JsObject(Seq( - "ID" -> JsNumber(asset.id), - "TAG" -> JsString(asset.tag), - "STATE" -> Json.toJson(State.findById(asset.stateId)), - "STATUS" -> JsString(asset.getStatusName), - "TYPE" -> Json.toJson(AssetType.findById(asset.assetTypeId).map(_.name)), - "CREATED" -> Json.toJson(asset.created), - "UPDATED" -> Json.toJson(asset.updated), - "DELETED" -> Json.toJson(asset.deleted) - )) - } + override def reads(json: JsValue) = JsSuccess(Asset( + (json \ "TAG").as[String], + Status.findByName((json \ "STATUS").as[String]).map(_.id).get, + AssetType.findByName((json \ "TYPE").as[String]).map(_.id).get, + (json \ "CREATED").as[Timestamp], + (json \ "UPDATED").asOpt[Timestamp], + (json \ "DELETED").asOpt[Timestamp], + (json \ "ID").as[Long], + (json \ "STATE").asOpt[State].map(_.id).getOrElse(0) + )) + override def writes(asset: AssetView): JsObject = JsObject(Seq( + "ID" -> JsNumber(asset.id), + "TAG" -> JsString(asset.tag), + "STATE" -> Json.toJson(State.findById(asset.stateId)), + "STATUS" -> JsString(asset.getStatusName), + "TYPE" -> Json.toJson(AssetType.findById(asset.assetTypeId).map(_.name)), + "CREATED" -> Json.toJson(asset.created), + "UPDATED" -> Json.toJson(asset.updated), + "DELETED" -> Json.toJson(asset.deleted) + )) } } diff --git a/app/collins/models/conversions.scala b/app/collins/models/conversions.scala index 3014ae6ba..e6e49616e 100644 --- a/app/collins/models/conversions.scala +++ b/app/collins/models/conversions.scala @@ -15,7 +15,6 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats import collins.util.IpAddress import collins.util.views.Formatter.ISO_8601_FORMAT import collins.util.views.Formatter.dateFormat @@ -31,9 +30,8 @@ object conversions { implicit def ops2bo(o: Option[String]) = new LogicalBooleanFromString(o) implicit def reOrLike[E <% StringExpression[String]](s: E) = new PossibleRegex(s) implicit def orderByString2oba[E <% TypedExpressionNode[_]](e: E) = new OrderByFromString(e) - implicit object TimestampFormat extends Format[Timestamp] { - override def reads(json: JsValue) = Stats.time("Timestamp.Reads") { + override def reads(json: JsValue) = JsSuccess(json.asOpt[String].filter(_.nonEmpty) .map { str => val formatter = new SimpleDateFormat(ISO_8601_FORMAT) @@ -41,97 +39,78 @@ object conversions { }.getOrElse { new Timestamp(0L) }) - } - override def writes(ts: Timestamp) = Stats.time("Timestamp.Writes") { - Json.toJson(dateFormat(ts)) - } + override def writes(ts: Timestamp) = Json.toJson(dateFormat(ts)) } - implicit object IpmiFormat extends Format[IpmiInfo] { import IpmiInfo.Enum._ - override def reads(json: JsValue) = Stats.time("Ipmi.Reads") { - JsSuccess(IpmiInfo( - (json \ "ASSET_ID").as[Long], - (json \ IpmiUsername.toString).as[String], - (json \ IpmiPassword.toString).as[String], - IpAddress.toLong((json \ IpmiGateway.toString).as[String]), - IpAddress.toLong((json \ IpmiAddress.toString).as[String]), - IpAddress.toLong((json \ IpmiNetmask.toString).as[String]), - (json \ "ID").asOpt[Long].getOrElse(0L) - )) - } - override def writes(ipmi: IpmiInfo) = Stats.time("Ipmi.Writes") { - JsObject(Seq( - "ASSET_ID" -> Json.toJson(ipmi.assetId), - "ASSET_TAG" -> Json.toJson(Asset.findById(ipmi.assetId).map(_.tag).getOrElse("Unknown")), - IpmiUsername.toString -> Json.toJson(ipmi.username), - IpmiPassword.toString -> Json.toJson(ipmi.password), - IpmiGateway.toString -> Json.toJson(ipmi.dottedGateway), - IpmiAddress.toString -> Json.toJson(ipmi.dottedAddress), - IpmiNetmask.toString -> Json.toJson(ipmi.dottedNetmask), - "ID" -> Json.toJson(ipmi.id) - )) - } + override def reads(json: JsValue) = JsSuccess(IpmiInfo( + (json \ "ASSET_ID").as[Long], + (json \ IpmiUsername.toString).as[String], + (json \ IpmiPassword.toString).as[String], + IpAddress.toLong((json \ IpmiGateway.toString).as[String]), + IpAddress.toLong((json \ IpmiAddress.toString).as[String]), + IpAddress.toLong((json \ IpmiNetmask.toString).as[String]), + (json \ "ID").asOpt[Long].getOrElse(0L) + )) + override def writes(ipmi: IpmiInfo) = JsObject(Seq( + "ASSET_ID" -> Json.toJson(ipmi.assetId), + "ASSET_TAG" -> Json.toJson(Asset.findById(ipmi.assetId).map(_.tag).getOrElse("Unknown")), + IpmiUsername.toString -> Json.toJson(ipmi.username), + IpmiPassword.toString -> Json.toJson(ipmi.password), + IpmiGateway.toString -> Json.toJson(ipmi.dottedGateway), + IpmiAddress.toString -> Json.toJson(ipmi.dottedAddress), + IpmiNetmask.toString -> Json.toJson(ipmi.dottedNetmask), + "ID" -> Json.toJson(ipmi.id) + )) } - implicit object IpAddressFormat extends Format[IpAddresses] { - override def reads(json: JsValue) = Stats.time("IpAddress.Reads") { - JsSuccess(IpAddresses( - (json \ "ASSET_ID").as[Long], - IpAddress.toLong((json \ "GATEWAY").as[String]), - IpAddress.toLong((json \ "ADDRESS").as[String]), - IpAddress.toLong((json \ "NETMASK").as[String]), - (json \ "POOL").asOpt[String].getOrElse(shared.IpAddressConfig.DefaultPoolName), - (json \ "ID").asOpt[Long].getOrElse(0L) - )) - } - override def writes(ip: IpAddresses) = Stats.time("IpAddress.Writes") { - JsObject(Seq( - "ASSET_ID" -> Json.toJson(ip.assetId), - "ASSET_TAG" -> Json.toJson(Asset.findById(ip.assetId).map(_.tag).getOrElse("Unknown")), - "GATEWAY" -> Json.toJson(ip.dottedGateway), - "ADDRESS" -> Json.toJson(ip.dottedAddress), - "NETMASK" -> Json.toJson(ip.dottedNetmask), - "POOL" -> Json.toJson(ip.pool), - "ID" -> Json.toJson(ip.id) - )) - } + override def reads(json: JsValue) = JsSuccess(IpAddresses( + (json \ "ASSET_ID").as[Long], + IpAddress.toLong((json \ "GATEWAY").as[String]), + IpAddress.toLong((json \ "ADDRESS").as[String]), + IpAddress.toLong((json \ "NETMASK").as[String]), + (json \ "POOL").asOpt[String].getOrElse(shared.IpAddressConfig.DefaultPoolName), + (json \ "ID").asOpt[Long].getOrElse(0L) + )) + override def writes(ip: IpAddresses) = JsObject(Seq( + "ASSET_ID" -> Json.toJson(ip.assetId), + "ASSET_TAG" -> Json.toJson(Asset.findById(ip.assetId).map(_.tag).getOrElse("Unknown")), + "GATEWAY" -> Json.toJson(ip.dottedGateway), + "ADDRESS" -> Json.toJson(ip.dottedAddress), + "NETMASK" -> Json.toJson(ip.dottedNetmask), + "POOL" -> Json.toJson(ip.pool), + "ID" -> Json.toJson(ip.id) + )) } - implicit object AssetLogFormat extends Format[AssetLog] { - override def reads(json: JsValue) = Stats.time("AssetLog.Reads") { - JsSuccess(AssetLog( - (json \ "ASSET_ID").as[Long], - (json \ "CREATED").as[Timestamp], - (json \ "CREATED_BY").as[String], - logs.LogFormat.withName((json \ "FORMAT").as[String]), - logs.LogSource.withName((json \ "SOURCE").as[String]), - logs.LogMessageType.withName((json \ "TYPE").as[String]), - (json \ "MESSAGE").as[String], - (json \ "ID").asOpt[Long].getOrElse(0L) - )) - } - - override def writes(log: AssetLog) = Stats.time("AssetLog.Writes") { - JsObject(Seq( - "ID" -> Json.toJson(log.id), - "ASSET_TAG" -> Json.toJson(Asset.findById(log.assetId).map(_.tag).getOrElse("Unknown")), - "CREATED" -> Json.toJson(log.created), - "CREATED_BY" -> Json.toJson(log.createdBy), - "FORMAT" -> Json.toJson(log.format.toString), - "SOURCE" -> Json.toJson(log.source.toString), - "TYPE" -> Json.toJson(log.messageType.toString), - "MESSAGE" -> (if (log.isJson()) { - try { - Json.parse(log.message) - } catch { - case e: Throwable => Json.toJson("Error parsing JSON: %s".format(e.getMessage)) - } - } else { - Json.toJson(log.message) - }) - )) - } + override def reads(json: JsValue) = JsSuccess(AssetLog( + (json \ "ASSET_ID").as[Long], + (json \ "CREATED").as[Timestamp], + (json \ "CREATED_BY").as[String], + logs.LogFormat.withName((json \ "FORMAT").as[String]), + logs.LogSource.withName((json \ "SOURCE").as[String]), + logs.LogMessageType.withName((json \ "TYPE").as[String]), + (json \ "MESSAGE").as[String], + (json \ "ID").asOpt[Long].getOrElse(0L) + )) + override def writes(log: AssetLog) = JsObject(Seq( + "ID" -> Json.toJson(log.id), + "ASSET_TAG" -> Json.toJson(Asset.findById(log.assetId).map(_.tag).getOrElse("Unknown")), + "CREATED" -> Json.toJson(log.created), + "CREATED_BY" -> Json.toJson(log.createdBy), + "FORMAT" -> Json.toJson(log.format.toString), + "SOURCE" -> Json.toJson(log.source.toString), + "TYPE" -> Json.toJson(log.messageType.toString), + "MESSAGE" -> (if (log.isJson()) { + try { + Json.parse(log.message) + } catch { + case e: Throwable => Json.toJson("Error parsing JSON: %s".format(e.getMessage)) + } + } else { + Json.toJson(log.message) + }) + )) } } diff --git a/app/collins/models/lldp/Chassis.scala b/app/collins/models/lldp/Chassis.scala index bf06ed3ae..33c7889ce 100644 --- a/app/collins/models/lldp/Chassis.scala +++ b/app/collins/models/lldp/Chassis.scala @@ -6,27 +6,21 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - import collins.models.lldp.ChassisId.ChassisIdFormat object Chassis { import ChassisId._ implicit object ChassisFormat extends Format[Chassis] { - override def reads(json: JsValue) = Stats.time("Chassis.Reads") { - JsSuccess(Chassis( - (json \ "NAME").as[String], - (json \ "ID").as[ChassisId], - (json \ "DESCRIPTION").as[String] - )) - } - override def writes(chassis: Chassis) = Stats.time("Chassis.Writes") { - JsObject(Seq( - "NAME" -> Json.toJson(chassis.name), - "ID" -> Json.toJson(chassis.id), - "DESCRIPTION" -> Json.toJson(chassis.description) - )) - } + override def reads(json: JsValue) = JsSuccess(Chassis( + (json \ "NAME").as[String], + (json \ "ID").as[ChassisId], + (json \ "DESCRIPTION").as[String] + )) + override def writes(chassis: Chassis) = JsObject(Seq( + "NAME" -> Json.toJson(chassis.name), + "ID" -> Json.toJson(chassis.id), + "DESCRIPTION" -> Json.toJson(chassis.description) + )) } } diff --git a/app/collins/models/lldp/ChassisId.scala b/app/collins/models/lldp/ChassisId.scala index 98bba0149..57ec402f9 100644 --- a/app/collins/models/lldp/ChassisId.scala +++ b/app/collins/models/lldp/ChassisId.scala @@ -6,22 +6,16 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - object ChassisId { implicit object ChassisIdFormat extends Format[ChassisId] { - override def reads(json: JsValue) = Stats.time("ChassisId.Reads") { - JsSuccess(ChassisId( - (json \ "TYPE").as[String], - (json \ "VALUE").as[String] - )) - } - override def writes(cid: ChassisId) = Stats.time("ChassisId.Writes") { - JsObject(Seq( - "TYPE" -> Json.toJson(cid.idType), - "VALUE" -> Json.toJson(cid.value) - )) - } + override def reads(json: JsValue) = JsSuccess(ChassisId( + (json \ "TYPE").as[String], + (json \ "VALUE").as[String] + )) + override def writes(cid: ChassisId) = JsObject(Seq( + "TYPE" -> Json.toJson(cid.idType), + "VALUE" -> Json.toJson(cid.value) + )) } } diff --git a/app/collins/models/lldp/Interface.scala b/app/collins/models/lldp/Interface.scala index 4f177c467..7f083f811 100644 --- a/app/collins/models/lldp/Interface.scala +++ b/app/collins/models/lldp/Interface.scala @@ -6,8 +6,6 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - import collins.models.lldp.Chassis.ChassisFormat import collins.models.lldp.Port.PortFormat import collins.models.lldp.Vlan.VlanFormat @@ -17,22 +15,18 @@ object Interface { import Port._ import Vlan._ implicit object InterfaceFormat extends Format[Interface] { - override def reads(json: JsValue) = Stats.time("Interface.Reads") { - JsSuccess(Interface( - (json \ "NAME").as[String], - (json \ "CHASSIS").as[Chassis], - (json \ "PORT").as[Port], - (json \ "VLANS").as[Seq[Vlan]] - )) - } - override def writes(iface: Interface) = Stats.time("Interface.Writes") { - JsObject(Seq( - "NAME" -> Json.toJson(iface.name), - "CHASSIS" -> Json.toJson(iface.chassis), - "PORT" -> Json.toJson(iface.port), - "VLANS" -> Json.toJson(iface.vlans) - )) - } + override def reads(json: JsValue) = JsSuccess(Interface( + (json \ "NAME").as[String], + (json \ "CHASSIS").as[Chassis], + (json \ "PORT").as[Port], + (json \ "VLANS").as[Seq[Vlan]] + )) + override def writes(iface: Interface) = JsObject(Seq( + "NAME" -> Json.toJson(iface.name), + "CHASSIS" -> Json.toJson(iface.chassis), + "PORT" -> Json.toJson(iface.port), + "VLANS" -> Json.toJson(iface.vlans) + )) } } diff --git a/app/collins/models/lldp/Port.scala b/app/collins/models/lldp/Port.scala index a8171725c..e7fad04a9 100644 --- a/app/collins/models/lldp/Port.scala +++ b/app/collins/models/lldp/Port.scala @@ -6,25 +6,19 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - import collins.models.lldp.PortId.PortIdFormat object Port { import PortId._ implicit object PortFormat extends Format[Port] { - override def reads(json: JsValue) = Stats.time("Port.Reads") { - JsSuccess(Port( - (json \ "ID").as[PortId], - (json \ "DESCRIPTION").as[String] - )) - } - override def writes(port: Port) = Stats.time("Port.Writes") { - JsObject(Seq( - "ID" -> Json.toJson(port.id), - "DESCRIPTION" -> Json.toJson(port.description) - )) - } + override def reads(json: JsValue) = JsSuccess(Port( + (json \ "ID").as[PortId], + (json \ "DESCRIPTION").as[String] + )) + override def writes(port: Port) = JsObject(Seq( + "ID" -> Json.toJson(port.id), + "DESCRIPTION" -> Json.toJson(port.description) + )) } } diff --git a/app/collins/models/lldp/PortId.scala b/app/collins/models/lldp/PortId.scala index c77749541..df9d239bd 100644 --- a/app/collins/models/lldp/PortId.scala +++ b/app/collins/models/lldp/PortId.scala @@ -6,22 +6,16 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - object PortId { implicit object PortIdFormat extends Format[PortId] { - override def reads(json: JsValue) = Stats.time("PortId.Reads") { - JsSuccess(PortId( - (json \ "TYPE").as[String], - (json \ "VALUE").as[String] - )) - } - override def writes(pid: PortId) = Stats.time("PortId.Writes") { - JsObject(Seq( - "TYPE" -> Json.toJson(pid.idType), - "VALUE" -> Json.toJson(pid.value) - )) - } + override def reads(json: JsValue) = JsSuccess(PortId( + (json \ "TYPE").as[String], + (json \ "VALUE").as[String] + )) + override def writes(pid: PortId) = JsObject(Seq( + "TYPE" -> Json.toJson(pid.idType), + "VALUE" -> Json.toJson(pid.value) + )) } } case class PortId(idType: String, value: String) extends LldpAttribute { diff --git a/app/collins/models/lldp/Vlan.scala b/app/collins/models/lldp/Vlan.scala index 37e73f42d..a270d0ffb 100644 --- a/app/collins/models/lldp/Vlan.scala +++ b/app/collins/models/lldp/Vlan.scala @@ -6,22 +6,16 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - object Vlan { implicit object VlanFormat extends Format[Vlan] { - override def reads(json: JsValue) = Stats.time("Vlan.Reads") { - JsSuccess(Vlan( - (json \ "ID").as[Int], - (json \ "NAME").as[String] - )) - } - override def writes(vlan: Vlan) = Stats.time("Vlan.Writes") { - JsObject(Seq( - "ID" -> Json.toJson(vlan.id), - "NAME" -> Json.toJson(vlan.name) - )) - } + override def reads(json: JsValue) = JsSuccess(Vlan( + (json \ "ID").as[Int], + (json \ "NAME").as[String] + )) + override def writes(vlan: Vlan) = JsObject(Seq( + "ID" -> Json.toJson(vlan.id), + "NAME" -> Json.toJson(vlan.name) + )) } } diff --git a/app/collins/models/lshw/Cpu.scala b/app/collins/models/lshw/Cpu.scala index 551b22329..adcd42237 100644 --- a/app/collins/models/lshw/Cpu.scala +++ b/app/collins/models/lshw/Cpu.scala @@ -6,31 +6,25 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - object Cpu { implicit object CpuFormat extends Format[Cpu] { - override def reads(json: JsValue) = Stats.time("Cpu.Reads") { - JsSuccess(Cpu( - (json \ "CORES").as[Int], - (json \ "THREADS").as[Int], - (json \ "SPEED_GHZ").as[Double], - (json \ "DESCRIPTION").as[String], - (json \ "PRODUCT").as[String], - (json \ "VENDOR").as[String] - )) - } - override def writes(cpu: Cpu) = Stats.time("Cpu.Writes") { - JsObject(Seq( - "CORES" -> Json.toJson(cpu.cores), - "THREADS" -> Json.toJson(cpu.threads), - "SPEED_GHZ" -> Json.toJson(cpu.speedGhz), - "DESCRIPTION" -> Json.toJson(cpu.description), - "PRODUCT" -> Json.toJson(cpu.product), - "VENDOR" -> Json.toJson(cpu.vendor) - )) - } + override def reads(json: JsValue) = JsSuccess(Cpu( + (json \ "CORES").as[Int], + (json \ "THREADS").as[Int], + (json \ "SPEED_GHZ").as[Double], + (json \ "DESCRIPTION").as[String], + (json \ "PRODUCT").as[String], + (json \ "VENDOR").as[String] + )) + override def writes(cpu: Cpu) = JsObject(Seq( + "CORES" -> Json.toJson(cpu.cores), + "THREADS" -> Json.toJson(cpu.threads), + "SPEED_GHZ" -> Json.toJson(cpu.speedGhz), + "DESCRIPTION" -> Json.toJson(cpu.description), + "PRODUCT" -> Json.toJson(cpu.product), + "VENDOR" -> Json.toJson(cpu.vendor) + )) } } diff --git a/app/collins/models/lshw/Disk.scala b/app/collins/models/lshw/Disk.scala index db32735ff..78c9e797a 100644 --- a/app/collins/models/lshw/Disk.scala +++ b/app/collins/models/lshw/Disk.scala @@ -6,7 +6,6 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats import collins.util.ByteStorageUnit object Disk { @@ -20,26 +19,22 @@ object Disk { } implicit object DiskFormat extends Format[Disk] { - override def reads(json: JsValue) = Stats.time("Disk.Reads") { - JsSuccess(Disk( - ByteStorageUnit((json \ "SIZE").as[Long]), - Disk.Type.withName((json \ "TYPE").as[String]), - (json \ "DESCRIPTION").as[String], - (json \ "PRODUCT").as[String], - (json \ "VENDOR").as[String] - )) - } - override def writes(disk: Disk) = Stats.time("Disk.Writes") { - JsObject(Seq( - "SIZE" -> Json.toJson(disk.size.inBytes), - "SIZE_S" -> Json.toJson(disk.size.inBytes.toString), - "SIZE_HUMAN" -> Json.toJson(disk.size.toHuman), - "TYPE" -> Json.toJson(disk.diskType.toString), - "DESCRIPTION" -> Json.toJson(disk.description), - "PRODUCT" -> Json.toJson(disk.product), - "VENDOR" -> Json.toJson(disk.vendor) - )) - } + override def reads(json: JsValue) = JsSuccess(Disk( + ByteStorageUnit((json \ "SIZE").as[Long]), + Disk.Type.withName((json \ "TYPE").as[String]), + (json \ "DESCRIPTION").as[String], + (json \ "PRODUCT").as[String], + (json \ "VENDOR").as[String] + )) + override def writes(disk: Disk) = JsObject(Seq( + "SIZE" -> Json.toJson(disk.size.inBytes), + "SIZE_S" -> Json.toJson(disk.size.inBytes.toString), + "SIZE_HUMAN" -> Json.toJson(disk.size.toHuman), + "TYPE" -> Json.toJson(disk.diskType.toString), + "DESCRIPTION" -> Json.toJson(disk.description), + "PRODUCT" -> Json.toJson(disk.product), + "VENDOR" -> Json.toJson(disk.vendor) + )) } } diff --git a/app/collins/models/lshw/Memory.scala b/app/collins/models/lshw/Memory.scala index 152f0534d..731af387d 100644 --- a/app/collins/models/lshw/Memory.scala +++ b/app/collins/models/lshw/Memory.scala @@ -6,31 +6,26 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats import collins.util.ByteStorageUnit object Memory { implicit object MemoryFormat extends Format[Memory] { - override def reads(json: JsValue) = Stats.time("Memory.Reads") { - JsSuccess(Memory( - ByteStorageUnit((json \ "SIZE").as[Long]), - (json \ "BANK").as[Int], - (json \ "DESCRIPTION").as[String], - (json \ "PRODUCT").as[String], - (json \ "VENDOR").as[String] - )) - } - override def writes(mem: Memory) = Stats.time("Memory.Writes") { - JsObject(Seq( - "SIZE" -> Json.toJson(mem.size.inBytes), - "SIZE_S" -> Json.toJson(mem.size.inBytes.toString), - "SIZE_HUMAN" -> Json.toJson(mem.size.toHuman), - "BANK" -> Json.toJson(mem.bank), - "DESCRIPTION" -> Json.toJson(mem.description), - "PRODUCT" -> Json.toJson(mem.product), - "VENDOR" -> Json.toJson(mem.vendor) - )) - } + override def reads(json: JsValue) = JsSuccess(Memory( + ByteStorageUnit((json \ "SIZE").as[Long]), + (json \ "BANK").as[Int], + (json \ "DESCRIPTION").as[String], + (json \ "PRODUCT").as[String], + (json \ "VENDOR").as[String] + )) + override def writes(mem: Memory) = JsObject(Seq( + "SIZE" -> Json.toJson(mem.size.inBytes), + "SIZE_S" -> Json.toJson(mem.size.inBytes.toString), + "SIZE_HUMAN" -> Json.toJson(mem.size.toHuman), + "BANK" -> Json.toJson(mem.bank), + "DESCRIPTION" -> Json.toJson(mem.description), + "PRODUCT" -> Json.toJson(mem.product), + "VENDOR" -> Json.toJson(mem.vendor) + )) } } diff --git a/app/collins/models/lshw/Nic.scala b/app/collins/models/lshw/Nic.scala index 36e5abe69..93b873ad2 100644 --- a/app/collins/models/lshw/Nic.scala +++ b/app/collins/models/lshw/Nic.scala @@ -6,31 +6,26 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats import collins.util.BitStorageUnit object Nic { implicit object NicFormat extends Format[Nic] { - override def reads(json: JsValue) = Stats.time("Nic.Reads") { - JsSuccess(Nic( - BitStorageUnit((json \ "SPEED").as[Long]), - (json \ "MAC_ADDRESS").as[String], - (json \ "DESCRIPTION").as[String], - (json \ "PRODUCT").as[String], - (json \ "VENDOR").as[String] - )) - } - override def writes(nic: Nic) = Stats.time("Nic.Writes") { - JsObject(Seq( - "SPEED" -> Json.toJson(nic.speed.inBits), - "SPEED_S" -> Json.toJson(nic.speed.inBits.toString), - "SPEED_HUMAN" -> Json.toJson(nic.speed.toHuman), - "MAC_ADDRESS" -> Json.toJson(nic.macAddress), - "DESCRIPTION" -> Json.toJson(nic.description), - "PRODUCT" -> Json.toJson(nic.product), - "VENDOR" -> Json.toJson(nic.vendor) - )) - } + override def reads(json: JsValue) = JsSuccess(Nic( + BitStorageUnit((json \ "SPEED").as[Long]), + (json \ "MAC_ADDRESS").as[String], + (json \ "DESCRIPTION").as[String], + (json \ "PRODUCT").as[String], + (json \ "VENDOR").as[String] + )) + override def writes(nic: Nic) = JsObject(Seq( + "SPEED" -> Json.toJson(nic.speed.inBits), + "SPEED_S" -> Json.toJson(nic.speed.inBits.toString), + "SPEED_HUMAN" -> Json.toJson(nic.speed.toHuman), + "MAC_ADDRESS" -> Json.toJson(nic.macAddress), + "DESCRIPTION" -> Json.toJson(nic.description), + "PRODUCT" -> Json.toJson(nic.product), + "VENDOR" -> Json.toJson(nic.vendor) + )) } } diff --git a/app/collins/models/lshw/ServerBase.scala b/app/collins/models/lshw/ServerBase.scala index 2546f5794..db3dc4f54 100644 --- a/app/collins/models/lshw/ServerBase.scala +++ b/app/collins/models/lshw/ServerBase.scala @@ -6,26 +6,20 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - object ServerBase { implicit object ServerbaseFormat extends Format[ServerBase] { - override def reads(json: JsValue) = Stats.time("Serverbase.Reads") { - JsSuccess(ServerBase( - (json \ "DESCRIPTION").as[String], - (json \ "PRODUCT").as[String], - (json \ "VENDOR").as[String], - (json \ "SERIAL").as[String] - )) - } - override def writes(serverbase: ServerBase) = Stats.time("Serverbase.Writes") { - JsObject(Seq( - "DESCRIPTION" -> Json.toJson(serverbase.description), - "PRODUCT" -> Json.toJson(serverbase.product), - "VENDOR" -> Json.toJson(serverbase.vendor), - "SERIAL" -> Json.toJson(serverbase.serial) - )) - } + override def reads(json: JsValue) = JsSuccess(ServerBase( + (json \ "DESCRIPTION").as[String], + (json \ "PRODUCT").as[String], + (json \ "VENDOR").as[String], + (json \ "SERIAL").as[String] + )) + override def writes(serverbase: ServerBase) = JsObject(Seq( + "DESCRIPTION" -> Json.toJson(serverbase.description), + "PRODUCT" -> Json.toJson(serverbase.product), + "VENDOR" -> Json.toJson(serverbase.vendor), + "SERIAL" -> Json.toJson(serverbase.serial) + )) } } diff --git a/app/collins/util/LldpRepresentation.scala b/app/collins/util/LldpRepresentation.scala index fdcc48fd7..bdab829ce 100644 --- a/app/collins/util/LldpRepresentation.scala +++ b/app/collins/util/LldpRepresentation.scala @@ -16,16 +16,12 @@ object LldpRepresentation { new LldpRepresentation(Seq()) } implicit object LldpFormat extends Format[LldpRepresentation] { - override def reads(json: JsValue) = Stats.time("LldpRepresentation.Reads") { - JsSuccess(LldpRepresentation( - (json \ "INTERFACES").as[Seq[Interface]] - )) - } - override def writes(lldp: LldpRepresentation) = Stats.time("LldpRepresentation.Writes") { - JsObject(Seq( - "INTERFACES" -> Json.toJson(lldp.interfaces) - )) - } + override def reads(json: JsValue) = JsSuccess(LldpRepresentation( + (json \ "INTERFACES").as[Seq[Interface]] + )) + override def writes(lldp: LldpRepresentation) = JsObject(Seq( + "INTERFACES" -> Json.toJson(lldp.interfaces) + )) } } diff --git a/app/collins/util/LshwRepresentation.scala b/app/collins/util/LshwRepresentation.scala index d16211e1e..ac22eede5 100644 --- a/app/collins/util/LshwRepresentation.scala +++ b/app/collins/util/LshwRepresentation.scala @@ -28,24 +28,20 @@ object LshwRepresentation { import Nic._ import ServerBase._ import Json.toJson - override def reads(json: JsValue) = Stats.time("LshwRepresentation.Reads") { - JsSuccess(LshwRepresentation( - (json \ "CPU").as[Seq[Cpu]], - (json \ "MEMORY").as[Seq[Memory]], - (json \ "NIC").as[Seq[Nic]], - (json \ "DISK").as[Seq[Disk]], - (json \ "BASE").as[ServerBase] - )) - } - override def writes(lshw: LshwRepresentation) = Stats.time("LshwRepresentation.Writes") { - JsObject(Seq( - "CPU" -> Json.toJson(lshw.cpus), - "MEMORY" -> Json.toJson(lshw.memory), - "NIC" -> Json.toJson(lshw.nics), - "DISK" -> Json.toJson(lshw.disks), - "BASE" -> Json.toJson(lshw.base) - )) - } + override def reads(json: JsValue) = JsSuccess(LshwRepresentation( + (json \ "CPU").as[Seq[Cpu]], + (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), + "MEMORY" -> Json.toJson(lshw.memory), + "NIC" -> Json.toJson(lshw.nics), + "DISK" -> Json.toJson(lshw.disks), + "BASE" -> Json.toJson(lshw.base) + )) } } diff --git a/app/collins/util/power/PowerComponent.scala b/app/collins/util/power/PowerComponent.scala index 82bcd9a40..82c505a82 100644 --- a/app/collins/util/power/PowerComponent.scala +++ b/app/collins/util/power/PowerComponent.scala @@ -6,8 +6,6 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - import collins.models.AssetMeta /** A power related component (distribution unit, strip, outlet, etc) */ @@ -16,7 +14,7 @@ sealed trait PowerComponent extends Ordered[PowerComponent] { def config: PowerConfiguration def id: Int // the id of the power unit // the position of the component within a unit, not physical position, this has to do with ordering during display - def position: Int + def position: Int def value: Option[String] // value of the power component def label = PowerConfiguration.Messages.ComponentLabel(typeName, sid) @@ -63,26 +61,22 @@ object PowerComponent { case false => s.split("_").last.charAt(0).toInt } implicit object PowerComponentFormat extends Format[PowerComponent] { - override def reads(json: JsValue) = Stats.time("PowerComponent.Reads") { - JsSuccess(PowerComponentValue( - Symbol(unidentify((json \ "TYPE").as[String])), - PowerConfiguration.get(), - unkey((json \ "KEY").as[String]), - (json \ "POSITION").as[Int], - (json \ "VALUE").asOpt[String] - )) - } + override def reads(json: JsValue) = JsSuccess(PowerComponentValue( + Symbol(unidentify((json \ "TYPE").as[String])), + PowerConfiguration.get(), + unkey((json \ "KEY").as[String]), + (json \ "POSITION").as[Int], + (json \ "VALUE").asOpt[String] + )) - override def writes(pc: PowerComponent) = Stats.time("PowerComponent.Writes") { - JsObject(Seq( - "KEY" -> Json.toJson(pc.key), - "VALUE" -> Json.toJson(pc.value.getOrElse("Unspecified")), - "TYPE" -> Json.toJson(pc.identifier), - "LABEL" -> Json.toJson(pc.label), - "POSITION" -> Json.toJson(pc.position), - "IS_REQUIRED" -> Json.toJson(pc.isRequired), - "UNIQUE" -> Json.toJson(pc.isUnique) - )) - } + override def writes(pc: PowerComponent) = JsObject(Seq( + "KEY" -> Json.toJson(pc.key), + "VALUE" -> Json.toJson(pc.value.getOrElse("Unspecified")), + "TYPE" -> Json.toJson(pc.identifier), + "LABEL" -> Json.toJson(pc.label), + "POSITION" -> Json.toJson(pc.position), + "IS_REQUIRED" -> Json.toJson(pc.isRequired), + "UNIQUE" -> Json.toJson(pc.isUnique) + )) } } diff --git a/app/collins/util/power/PowerUnit.scala b/app/collins/util/power/PowerUnit.scala index ede531cf5..353150057 100644 --- a/app/collins/util/power/PowerUnit.scala +++ b/app/collins/util/power/PowerUnit.scala @@ -6,8 +6,6 @@ import play.api.libs.json.JsSuccess import play.api.libs.json.JsValue import play.api.libs.json.Json -import collins.util.Stats - object PowerUnit { def apply(config: PowerConfiguration, id: Int): PowerUnit = { val components: PowerComponents = @@ -17,19 +15,15 @@ object PowerUnit { new PowerUnit(config, id, components) } implicit object PowerUnitFormat extends Format[PowerUnit] { - override def reads(json: JsValue) = Stats.time("PowerUnit.Reads") { - JsSuccess(PowerUnit( - PowerConfiguration.get(), - (json \ "UNIT_ID").as[Int], - (json \ "UNITS").as[Set[PowerComponent]] - )) - } - override def writes(unit: PowerUnit) = Stats.time("PowerUnit.Writes") { - JsObject(Seq( - "UNIT_ID" -> Json.toJson(unit.id), - "UNITS" -> Json.toJson(unit.components.map(Json.toJson(_))) - )) - } + override def reads(json: JsValue) = JsSuccess(PowerUnit( + PowerConfiguration.get(), + (json \ "UNIT_ID").as[Int], + (json \ "UNITS").as[Set[PowerComponent]] + )) + override def writes(unit: PowerUnit) = JsObject(Seq( + "UNIT_ID" -> Json.toJson(unit.id), + "UNITS" -> Json.toJson(unit.components.map(Json.toJson(_))) + )) } }