com.normation.inventory
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/apidata/NodeDetailLevel.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/apidata/NodeDetailLevel.scala
index 7b2d2bd8592..d11abfea6a5 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/apidata/NodeDetailLevel.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/apidata/NodeDetailLevel.scala
@@ -46,6 +46,7 @@ import net.liftweb.json.JsonDSL._
import org.joda.time.DateTime
sealed trait NodeDetailLevel {
+
def fields: Set[String]
/**
@@ -277,7 +278,7 @@ object NodeDetailLevel {
if (soft.isEmpty) {
JNothing
} else {
- val softwares = soft.toList.map { software =>
+ val softwares = soft.toList.sortBy(_.name).map { software =>
("name" -> software.name) ~
("editor" -> software.editor.map(_.name)) ~
("version" -> software.version.map(_.value)) ~
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/batch/CheckInventoryUpdate.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/batch/CheckInventoryUpdate.scala
deleted file mode 100644
index 468ecc2dafc..00000000000
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/batch/CheckInventoryUpdate.scala
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- *************************************************************************************
- * Copyright 2017 Normation SAS
- *************************************************************************************
- *
- * This file is part of Rudder.
- *
- * Rudder is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * In accordance with the terms of section 7 (7. Additional Terms.) of
- * the GNU General Public License version 3, the copyright holders add
- * the following Additional permissions:
- * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
- * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
- * Public License version 3, when you create a Related Module, this
- * Related Module is not considered as a part of the work and may be
- * distributed under the license agreement of your choice.
- * A "Related Module" means a set of sources files including their
- * documentation that, without modification of the Source Code, enables
- * supplementary functions or services in addition to those offered by
- * the Software.
- *
- * Rudder is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Rudder. If not, see .
-
- *
- *************************************************************************************
- */
-
-package com.normation.rudder.batch
-
-import com.normation.errors.IOResult
-import com.normation.eventlog.ModificationId
-import com.normation.rudder.domain.eventlog.RudderEventActor
-import com.normation.rudder.domain.logger.ScheduledJobLoggerPure
-import com.normation.rudder.services.nodes.NodeInfoServiceCachedImpl
-import com.normation.utils.StringUuidGenerator
-import com.normation.zio._
-import org.joda.time.DateTime
-import scala.annotation.nowarn
-import zio._
-
-/**
- * A scheduler which checks every N seconds if inventories are updated.
- * If so, I will trigger a promise generation.
- *
- * Note that without this scheduler, if nobody needs the nodeInfoCache, it will never
- * be updated.
- */
-class CheckInventoryUpdate(
- nodeInfoCacheImpl: NodeInfoServiceCachedImpl,
- asyncDeploymentAgent: AsyncDeploymentActor,
- uuidGen: StringUuidGenerator,
- updateInterval: Duration
-) {
-
- // we need to store the time of last modification we saw so that is someone else update the cache
- // (for example, a user is using the UI), then even if the cache is upToDate, we now we have to start
- // a policy generation.
- val lastUpdate = Ref.make(new DateTime(0)).runNow
-
- val logger = ScheduledJobLoggerPure
- // start batch
- if (updateInterval < 1.second) {
- logger.logEffect.info(s"Disable automatic check for node inventories main information updates (update interval less than 1s)")
- } else {
- logger.logEffect.trace(
- s"***** starting check of node main inventories information update to trigger policy generation, every ${updateInterval.toString()} *****"
- )
- }
-
- // type annotation is necessary to avoid a "Any was infered, perhaps an error"
- val prog: UIO[Unit] = {
- (for {
- _ <- nodeInfoCacheImpl.updateCache()
- cacheTime <- nodeInfoCacheImpl.getCacheLastUpdate
- lastSeen <- lastUpdate.getAndUpdate(_ => cacheTime)
- _ <- if (lastSeen.isBefore(cacheTime)) {
- logger.info("Update in node inventories main information detected: triggering a policy generation") *>
- IOResult.attempt(asyncDeploymentAgent ! AutomaticStartDeployment(ModificationId(uuidGen.newUuid), RudderEventActor))
- } else {
- logger.trace("No update in node inventories main information detected")
- }
- } yield ()).catchAll(err =>
- logger.error(s"Error when trying to update node inventories information. Error is: ${err.fullMsg}")
- )
- }
-
- ZioRuntime.unsafeRun(
- prog.repeat(Schedule.fixed(updateInterval)).delay(30.seconds).forkDaemon
- ): @nowarn(
- "msg=a type was inferred to be `\\w+`; this may indicate a programming error."
- )
-
-}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/logger/ApplicationLogger.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/logger/ApplicationLogger.scala
index 7e1dc208cc8..3cf701d4bea 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/logger/ApplicationLogger.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/logger/ApplicationLogger.scala
@@ -147,6 +147,14 @@ object NodeLoggerPure extends NamedZioLogger { parent =>
def loggerName: String = parent.loggerName + ".cache"
}
+ object Metrics extends NamedZioLogger {
+ def loggerName: String = parent.loggerName + ".metrics"
+ }
+
+ object Details extends NamedZioLogger {
+ def loggerName: String = parent.loggerName + ".details"
+ }
+
object PendingNode extends NamedZioLogger {
// the logger for information about pending nodes (accept/refuse)
def loggerName: String = parent.loggerName + ".pending"
@@ -228,8 +236,12 @@ object ReportLogger extends Logger {
}
}
-object FactQueryProcessorPure extends NamedZioLogger {
+object FactQueryProcessorLoggerPure extends NamedZioLogger {
override def loggerName: String = "query.node-fact"
+
+ object Metrics extends NamedZioLogger {
+ override def loggerName: String = FactQueryProcessorLoggerPure.loggerName + ".metrics"
+ }
}
object ReportLoggerPure extends NamedZioLogger {
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/CmdbQuery.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/CmdbQuery.scala
index 539cf95445b..b69a2c1db44 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/CmdbQuery.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/CmdbQuery.scala
@@ -103,6 +103,42 @@ object KeyValueComparator {
def values = ca.mrvisser.sealerate.values[KeyValueComparator]
}
+object NodePropertyMatcherUtils {
+
+ // split k=v (v may not exists if there is no '='
+ // is there is several '=', we consider they are part of the value
+ def splitInput(value: String, sep: String): SplittedValue = {
+ val array = value.split(sep)
+ val k = array(0) // always exists with split
+ val v = array.toList.tail
+ SplittedValue(k, v, sep)
+ }
+
+ def matchJsonPath(key: String, path: PureResult[JsonPath])(p: NodeProperty): Boolean = {
+ (p.name == key) && path.flatMap(JsonSelect.exists(_, p.valueAsString).toPureResult).getOrElse(false)
+ }
+
+ val regexMatcher = (value: String) => {
+ new NodeInfoMatcher {
+ override val debugString = s"Prop matches '${value}'"
+
+ override def matches(node: NodeInfo): Boolean = matchesRegex(value, node.properties)
+ }
+ }
+
+ def matchesRegex(value: String, properties: Iterable[NodeProperty]) = {
+ val predicat = (p: NodeProperty) => {
+ try {
+ value.r.pattern.matcher(s"${p.name}=${p.valueAsString}").matches()
+ } catch { // malformed patterned should not be saved, but never let an exception be silent
+ case ex: PatternSyntaxException => false
+ }
+ }
+ properties.exists(predicat)
+ }
+
+}
+
sealed trait ComparatorList {
def comparators: Seq[CriterionComparator]
@@ -189,6 +225,9 @@ object NodeInfoMatcher {
}
}
+/************************* old matcher logic **********************************/
+// we will need to only keep the rendering part //
+
/*
* Below goes all NodeInfo Criterion Type
*/
@@ -363,22 +402,13 @@ case object NodeIpListComparator extends NodeCriterionType {
* {"name":"k","value":{ "any":"json","here":"here"}}
*
*/
-final case class SplittedValue(key: String, values: List[String]) {
- def value = values.mkString("=")
+final case class SplittedValue(key: String, values: List[String], separator: String = "=") {
+ def value = values.mkString(separator)
}
final case class NodePropertyComparator(ldapAttr: String) extends NodeCriterionType {
override val comparators = KeyValueComparator.values.toList ++ BaseComparators.comparators
- // split k=v (v may not exists if there is no '='
- // is there is several '=', we consider they are part of the value
- def splitInput(value: String, sep: String): SplittedValue = {
- val array = value.split(sep)
- val k = array(0) // always exists with split
- val v = array.toList.tail
- SplittedValue(k, v)
- }
-
override def validateSubCase(value: String, comparator: CriterionComparator): PureResult[String] = {
comparator match {
case Equals | NotEquals =>
@@ -408,31 +438,13 @@ final case class NodePropertyComparator(ldapAttr: String) extends NodeCriterionT
}
}
- def matchJsonPath(key: String, path: PureResult[JsonPath])(p: NodeProperty): Boolean = {
- (p.name == key) && path.flatMap(JsonSelect.exists(_, p.valueAsString).toPureResult).getOrElse(false)
- }
-
- val regexMatcher = (value: String) => {
- new NodeInfoMatcher {
- val predicat = (p: NodeProperty) => {
- try {
- value.r.pattern.matcher(s"${p.name}=${p.valueAsString}").matches()
- } catch { // malformed patterned should not be saved, but never let an exception be silent
- case ex: PatternSyntaxException => false
- }
- }
- override val debugString = s"Prop matches '${value}'"
- override def matches(node: NodeInfo): Boolean = node.properties.exists(predicat)
- }
- }
-
override def matches(comparator: CriterionComparator, value: String): NodeInfoMatcher = {
import com.normation.rudder.domain.queries.{KeyValueComparator => KVC}
comparator match {
// equals mean: the key is equals to kv._1 and the value is defined and the value is equals to kv._2.get
case Equals => {
- val kv = splitInput(value, "=")
+ val kv = NodePropertyMatcherUtils.splitInput(value, "=")
NodeInfoMatcher(
s"Prop name=value equals'${value}'",
(node: NodeInfo) => node.properties.find(p => p.name == kv.key && p.valueAsString == kv.value).isDefined
@@ -445,19 +457,19 @@ final case class NodePropertyComparator(ldapAttr: String) extends NodeCriterionT
NodeInfoMatcher(s"Prop name=value exists (at least one property)", (node: NodeInfo) => node.properties.nonEmpty)
case NotExists =>
NodeInfoMatcher(s"Prop name=value not exists (empty properties)", (node: NodeInfo) => node.properties.isEmpty)
- case Regex => regexMatcher(value)
+ case Regex => NodePropertyMatcherUtils.regexMatcher(value)
case NotRegex =>
new NodeInfoMatcher {
- val regex = regexMatcher(value)
+ val regex = NodePropertyMatcherUtils.regexMatcher(value)
override val debugString = s"Prop matches regex '${value}'"
override def matches(node: NodeInfo): Boolean = !regex.matches(node)
}
case KVC.HasKey => NodeInfoMatcher(s"Prop has key '${value}'", (node: NodeInfo) => node.properties.exists(_.name == value))
case KVC.JsonSelect =>
new NodeInfoMatcher {
- val kv = splitInput(value, ":")
+ val kv = NodePropertyMatcherUtils.splitInput(value, ":")
val path = JsonSelect.compilePath(kv.value).toPureResult
- val matcher = matchJsonPath(kv.key, path) _
+ val matcher = NodePropertyMatcherUtils.matchJsonPath(kv.key, path) _
override val debugString = s"Prop json select '${value}'"
override def matches(node: NodeInfo): Boolean = node.properties.exists(matcher)
}
@@ -886,11 +898,10 @@ final case class NameValueComparator(ldapAttr: String) extends TStringComparator
// split k=v (v may not exists if there is no '='
// is there is several '=', we consider they are part of the value
def splitInput(value: String): (String, Option[String]) = {
- val array = value.split('=')
- val k = array(0) // always exists with split
- val v = array.toList.tail match {
+ val SplittedValue(k, l, s) = NodePropertyMatcherUtils.splitInput(value, "=")
+ val v = l match {
case Nil => None
- case t => Some(t.mkString("="))
+ case t => Some(t.mkString(s))
}
(k, v)
}
@@ -937,7 +948,8 @@ final case class NameValueComparator(ldapAttr: String) extends TStringComparator
*/
final case class SubGroupChoice(id: NodeGroupId, name: String)
-class SubGroupComparator(getGroups: () => IOResult[Seq[SubGroupChoice]]) extends TStringComparator {
+// we must use `() => IOResult[...]` to avoid cyclic reference
+final case class SubGroupComparator(subGroupComparatorRepo: () => SubGroupComparatorRepository) extends TStringComparator {
override val comparators = Equals :: Nil
override def buildFilter(attributeName: String, comparator: CriterionComparator, value: String): Filter = comparator match {
@@ -949,7 +961,7 @@ class SubGroupComparator(getGroups: () => IOResult[Seq[SubGroupChoice]]) extends
// we need to query for the list of groups here
val subGroups: Seq[SelectableOption[String]] = {
(for {
- res <- getGroups()
+ res <- subGroupComparatorRepo().getGroups
} yield {
val g = res.map { case SubGroupChoice(id, name) => SelectableOption(id.serialize, name) }
// if current value is defined but not in the list, add it with a "missing group" label
@@ -986,7 +998,12 @@ class SubGroupComparator(getGroups: () => IOResult[Seq[SubGroupChoice]]) extends
* on an inventory (or successlly on an inventory) property but on a RudderNode property.
* In that case, give the predicat that the node must follows.
*/
-final case class Criterion(val name: String, val cType: CriterionType, overrideObjectType: Option[String] = None) {
+final case class Criterion(
+ val name: String,
+ val cType: CriterionType,
+ nodeCriterionMatcher: NodeCriterionMatcher,
+ overrideObjectType: Option[String] = None
+) {
require(name != null && name.nonEmpty, "Criterion name must be defined")
require(cType != null, "Criterion Type must be defined")
}
@@ -995,7 +1012,7 @@ case class ObjectCriterion(val objectType: String, val criteria: Seq[Criterion])
require(objectType.nonEmpty, "Unique identifier for line must be defined")
require(criteria.nonEmpty, "You must at least have one criterion for the line")
- // optionnaly retrieve the criterion from a "string" attribute
+ // optionally retrieve the criterion from a "string" attribute
def criterionForName(name: String): (Option[Criterion]) = {
for (c <- criteria) {
if (name.equalsIgnoreCase(c.name)) return Some(c)
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/DitQueryData.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/DitQueryData.scala
index e25d3971692..23651018477 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/DitQueryData.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/DitQueryData.scala
@@ -37,16 +37,12 @@
package com.normation.rudder.domain.queries
-import com.normation.errors._
import com.normation.inventory.ldap.core._
import com.normation.inventory.ldap.core.LDAPConstants._
import com.normation.ldap.sdk._
import com.normation.ldap.sdk.BuildFilter._
import com.normation.rudder.domain.NodeDit
import com.normation.rudder.domain.RudderDit
-import com.normation.rudder.domain.RudderLDAPConstants.A_NODE_GROUP_UUID
-import com.normation.rudder.domain.RudderLDAPConstants.A_NODE_PROPERTY
-import com.normation.rudder.domain.RudderLDAPConstants.A_STATE
import com.normation.rudder.domain.RudderLDAPConstants.OC_RUDDER_NODE_GROUP
import com.normation.rudder.services.queries.SpecialFilter
import com.unboundid.ldap.sdk.DN
@@ -103,237 +99,9 @@ case object NodeDnJoin extends LDAPJoinElement(A_NODE_UUID)
//request for that object type.
final case class LDAPObjectTypeFilter(value: Filter) extends AnyVal
-class DitQueryData(dit: InventoryDit, nodeDit: NodeDit, rudderDit: RudderDit, getGroups: () => IOResult[Seq[SubGroupChoice]]) {
- private val peObjectCriterion = ObjectCriterion(
- OC_PE,
- Seq(
- // Criterion(A_MACHINE_UUID, StringComparator),
- // Criterion(A_MACHINE_DN, StringComparator), //we don't want to search on that
- Criterion(A_DESCRIPTION, StringComparator),
- Criterion(A_MODEL, StringComparator),
- Criterion(A_SERIAL_NUMBER, StringComparator),
- Criterion(A_FIRMWARE, StringComparator),
- Criterion(A_QUANTITY, LongComparator),
- Criterion(A_SME_TYPE, StringComparator),
- Criterion(A_STATUS, LongComparator),
- Criterion(A_MANUFACTURER, StringComparator)
- )
- )
-
- private val leObjectCriterion = ObjectCriterion(
- OC_LE,
- Seq(
-// Criterion(A_NODE_UUID, StringComparator),
-// Criterion(A_NODE_DN, StringComparator),
-// Criterion(A_NAME, StringComparator),
- Criterion(A_DESCRIPTION, StringComparator)
- )
- )
-
- private val licenseObjectCriterion = ObjectCriterion(
- "licence",
- Seq(
- Criterion(A_LICENSE_EXP, DateComparator),
- Criterion(A_LICENSE_NAME, StringComparator),
- Criterion(A_LICENSE_PRODUCT_ID, StringComparator),
- Criterion(A_LICENSE_PRODUCT_KEY, StringComparator)
- )
- );
-
- protected val criteriaSet = Set(
- ObjectCriterion(
- OC_MACHINE,
- Seq(
- Criterion("machineType", MachineComparator),
- Criterion("provider", VmTypeComparator),
- Criterion(A_MACHINE_UUID, StringComparator),
- Criterion(A_NAME, StringComparator),
- Criterion(A_DESCRIPTION, StringComparator),
- Criterion(A_MB_UUID, StringComparator),
- Criterion(A_MANUFACTURER, StringComparator),
- Criterion(A_SERIAL_NUMBER, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_MEMORY,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_MEMORY_SLOT_NUMBER, LongComparator),
- Criterion(A_NAME, StringComparator),
- Criterion(A_MEMORY_CAPACITY, MemoryComparator),
- Criterion(A_MEMORY_CAPTION, StringComparator),
- Criterion(A_MEMORY_SPEED, LongComparator),
- Criterion(A_MEMORY_TYPE, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_STORAGE,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_STORAGE_NAME, StringComparator),
- Criterion(A_STORAGE_SIZE, MemoryComparator),
- Criterion(A_STORAGE_FIRMWARE, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_BIOS,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_BIOS_NAME, StringComparator),
- Criterion(A_RELEASE_DATE, StringComparator),
- Criterion(A_EDITOR, StringComparator),
- Criterion(A_SOFT_VERSION, StringComparator)
- ) ++
- licenseObjectCriterion.criteria
- ),
- ObjectCriterion(
- OC_CONTROLLER,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_CONTROLLER_NAME, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_PORT,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_PORT_NAME, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_PROCESSOR,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_PROCESSOR_NAME, StringComparator),
- Criterion(A_PROCESSOR_SPEED, LongComparator),
- Criterion(A_PROCESSOR_STEPPING, StringComparator),
- Criterion(A_PROCESSOR_FAMILLY, StringComparator),
- Criterion(A_PROCESSOR_FAMILY_NAME, StringComparator),
- Criterion(A_THREAD, StringComparator),
- Criterion(A_CORE, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_SLOT,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_SLOT_NAME, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_SOUND,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_SOUND_NAME, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_VIDEO,
- peObjectCriterion.criteria ++ Seq(
- Criterion(A_VIDEO_NAME, StringComparator),
- Criterion(A_VIDEO_CHIPSET, StringComparator),
- Criterion(A_VIDEO_RESOLUTION, StringComparator),
- Criterion(A_MEMORY_CAPACITY, MemoryComparator)
- )
- ),
- ObjectCriterion(
- OC_NODE,
- Seq(
- Criterion("OS", NodeOstypeComparator),
- Criterion(A_NODE_UUID, NodeStringComparator(node => node.node.id.value)),
- Criterion(A_HOSTNAME, NodeStringComparator(node => node.hostname)),
- Criterion(A_OS_NAME, NodeOsNameComparator),
- Criterion(A_OS_FULL_NAME, OrderedStringComparator),
- Criterion(A_OS_VERSION, OrderedStringComparator),
- Criterion(A_OS_SERVICE_PACK, OrderedStringComparator),
- Criterion(A_OS_KERNEL_VERSION, OrderedStringComparator),
- Criterion(A_ARCH, StringComparator),
- Criterion(A_STATE, NodeStateComparator, Some("rudderNode")),
- Criterion(A_OS_RAM, MemoryComparator),
- Criterion(A_OS_SWAP, MemoryComparator),
- Criterion(A_AGENTS_NAME, AgentComparator),
- Criterion(A_ACCOUNT, StringComparator),
- Criterion(A_LIST_OF_IP, NodeIpListComparator),
- Criterion(A_ROOT_USER, NodeStringComparator(node => node.localAdministratorAccountName)),
- Criterion(A_INVENTORY_DATE, DateComparator),
- Criterion(A_POLICY_SERVER_UUID, NodeStringComparator(node => node.policyServerId.value))
- )
- ),
- ObjectCriterion(
- OC_SOFTWARE,
- Seq(
- Criterion(A_NAME, StringComparator),
- Criterion(A_DESCRIPTION, StringComparator),
- Criterion(A_SOFT_VERSION, StringComparator),
- Criterion(A_RELEASE_DATE, DateComparator),
- Criterion(A_EDITOR, EditorComparator)
- ) ++
- licenseObjectCriterion.criteria
- ),
- ObjectCriterion(
- OC_NET_IF,
- leObjectCriterion.criteria ++ Seq(
- Criterion(A_NETWORK_NAME, StringComparator),
- Criterion(A_NETIF_ADDRESS, StringComparator),
- Criterion(A_NETIF_DHCP, StringComparator),
- Criterion(A_NETIF_GATEWAY, StringComparator),
- Criterion(A_NETIF_MASK, StringComparator),
- Criterion(A_NETIF_SUBNET, StringComparator),
- Criterion(A_NETIF_MAC, StringComparator),
- Criterion(A_NETIF_TYPE, StringComparator),
- Criterion(A_NETIF_TYPE_MIB, StringComparator)
- )
- ),
- ObjectCriterion(
- OC_FS,
- leObjectCriterion.criteria ++ Seq(
- Criterion(A_NAME, StringComparator),
- Criterion(A_MOUNT_POINT, StringComparator),
- Criterion(A_FILE_COUNT, StringComparator),
- Criterion(A_FREE_SPACE, MemoryComparator),
- Criterion(A_TOTAL_SPACE, MemoryComparator)
- )
- ),
- ObjectCriterion(
- A_PROCESS,
- Seq(
- Criterion("pid", JsonFixedKeyComparator(A_PROCESS, "pid", false)),
- Criterion("commandName", JsonFixedKeyComparator(A_PROCESS, "commandName", true)),
- Criterion("cpuUsage", JsonFixedKeyComparator(A_PROCESS, "cpuUsage", false)),
- Criterion("memory", JsonFixedKeyComparator(A_PROCESS, "memory", false)),
- Criterion("tty", JsonFixedKeyComparator(A_PROCESS, "tty", true)),
- Criterion("virtualMemory", JsonFixedKeyComparator(A_PROCESS, "virtualMemory", false)),
- Criterion("started", JsonFixedKeyComparator(A_PROCESS, "started", true)),
- Criterion("user", JsonFixedKeyComparator(A_PROCESS, "user", true))
- )
- ),
- ObjectCriterion(
- OC_VM_INFO,
- leObjectCriterion.criteria ++ Seq(
- Criterion(A_VM_TYPE, StringComparator),
- Criterion(A_VM_OWNER, StringComparator),
- Criterion(A_VM_STATUS, StringComparator),
- Criterion(A_VM_CPU, LongComparator),
- Criterion(A_VM_MEMORY, LongComparator),
- Criterion(A_VM_ID, StringComparator),
- Criterion(A_VM_SUBSYSTEM, StringComparator),
- Criterion(A_VM_NAME, StringComparator)
- )
- ),
- ObjectCriterion(
- A_EV,
- Seq(
- Criterion("name.value", NameValueComparator(A_EV))
- )
- ),
- ObjectCriterion(
- A_NODE_PROPERTY,
- Seq(
- Criterion("name.value", NodePropertyComparator(A_NODE_PROPERTY))
- )
- ),
- ObjectCriterion(
- "group",
- Seq(
- Criterion(A_NODE_GROUP_UUID, new SubGroupComparator(getGroups))
- )
- )
- )
+class DitQueryData(dit: InventoryDit, nodeDit: NodeDit, rudderDit: RudderDit, criteria: NodeQueryCriteriaData) {
- val criteriaMap: SortedMap[String, ObjectCriterion] =
- SortedMap[String, ObjectCriterion]() ++ (criteriaSet map { crit => (crit.objectType, crit) })
+ val criteriaMap: SortedMap[String, ObjectCriterion] = criteria.criteriaMap
/*
* * "baseDn" of the object type to search for
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/NodeQueryCriteriaData.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/NodeQueryCriteriaData.scala
new file mode 100644
index 00000000000..9d1e07521d3
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/domain/queries/NodeQueryCriteriaData.scala
@@ -0,0 +1,733 @@
+/*
+ *************************************************************************************
+ * Copyright 2011 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.domain.queries
+
+import com.normation.errors._
+import com.normation.inventory.domain.AgentType
+import com.normation.inventory.domain.NodeId
+import com.normation.inventory.ldap.core.LDAPConstants._
+import com.normation.inventory.ldap.core.LDAPConstants.A_PROCESS
+import com.normation.rudder.domain.RudderLDAPConstants.A_NODE_GROUP_UUID
+import com.normation.rudder.domain.RudderLDAPConstants.A_NODE_PROPERTY
+import com.normation.rudder.domain.RudderLDAPConstants.A_STATE
+import com.normation.rudder.domain.logger.FactQueryProcessorLoggerPure
+import com.normation.rudder.domain.nodes.NodeGroupId
+import com.normation.rudder.domain.properties.NodeProperty
+import com.normation.rudder.domain.queries.{KeyValueComparator => KVC}
+import com.normation.rudder.domain.queries.KeyValueComparator.HasKey
+import com.normation.rudder.facts.nodes.CoreNodeFact
+import com.normation.rudder.facts.nodes.NodeFact
+import com.normation.rudder.repository.RoNodeGroupRepository
+import com.normation.utils.DateFormaterService
+import java.util.function.Predicate
+import java.util.regex.Pattern
+import org.joda.time.DateTime
+import org.joda.time.format.DateTimeFormat
+import scala.collection.SortedMap
+import scala.util.Try
+import zio._
+import zio.syntax._
+
+trait SubGroupComparatorRepository {
+ def getNodeIds(groupId: NodeGroupId): IOResult[Chunk[NodeId]]
+ def getGroups: IOResult[Chunk[SubGroupChoice]]
+}
+// default implementation out of test use GroupRepo for that
+class DefaultSubGroupComparatorRepository(repo: RoNodeGroupRepository) extends SubGroupComparatorRepository {
+
+ override def getNodeIds(groupId: NodeGroupId): IOResult[Chunk[NodeId]] = {
+ repo.getNodeGroupOpt(groupId).map {
+ case None => Chunk.empty
+ case Some((group, _)) => Chunk.fromIterable(group.serverList)
+ }
+ }
+
+ override def getGroups: IOResult[Chunk[SubGroupChoice]] = {
+ repo.getAll().map(seq => Chunk.fromIterable(seq).map(g => SubGroupChoice(g.id, g.name)))
+ }
+}
+
+// groupRepo must be `=> ` to avoid cyclic dep
+class NodeQueryCriteriaData(groupRepo: () => SubGroupComparatorRepository) {
+
+ implicit class IterableToChunk[A](it: Iterable[A]) {
+ def toChunk: Chunk[A] = Chunk.fromIterable(it)
+ }
+
+ implicit class OptionToChunk[A](opt: Option[A]) {
+ def toChunk: Chunk[A] = Chunk.fromIterable(opt)
+ }
+
+ implicit class OneToChunk[A](a: A) {
+ def wrap: Chunk[A] = Chunk(a)
+ }
+
+ val criteria = Chunk(
+ ObjectCriterion(
+ OC_MACHINE,
+ Chunk(
+ Criterion("machineType", MachineComparator, NodeCriterionMatcherString(_.machine.machineType.kind.wrap)),
+ Criterion(A_MACHINE_UUID, StringComparator, NodeCriterionMatcherString(_.machine.id.value.wrap)),
+ Criterion(A_NAME, StringComparator, AlwaysFalse("machine does not have a 'name' attribute in fusion")),
+ Criterion(A_DESCRIPTION, StringComparator, AlwaysFalse("machine does not have a 'description' attribute in fusion")),
+ Criterion(A_MB_UUID, StringComparator, AlwaysFalse("machine does not have a 'mother board uuid' attribute in fusion")),
+ Criterion(
+ A_MANUFACTURER,
+ StringComparator,
+ AlwaysFalse("machine does not have a 'mother board uuid' attribute in fusion")
+ ),
+ Criterion(A_SERIAL_NUMBER, StringComparator, NodeCriterionMatcherString(_.machine.systemSerial.toChunk))
+ )
+ ),
+ ObjectCriterion(
+ OC_MEMORY,
+ Chunk(
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MEMORY_CAPACITY, MemoryComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MEMORY_CAPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MEMORY_SPEED, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MEMORY_SLOT_NUMBER, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MEMORY_TYPE, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SERIAL_NUMBER, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_STORAGE,
+ Chunk(
+ Criterion(A_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MODEL, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SERIAL_NUMBER, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_FIRMWARE, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SME_TYPE, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MANUFACTURER, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_STORAGE_SIZE, MemoryComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_STORAGE_FIRMWARE, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_BIOS,
+ Chunk(
+ Criterion(A_BIOS_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SOFT_VERSION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_RELEASE_DATE, DateComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_EDITOR, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_CONTROLLER,
+ Chunk(
+ Criterion(A_CONTROLLER_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SME_TYPE, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MANUFACTURER, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_PORT,
+ Chunk(
+ Criterion(A_PORT_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SME_TYPE, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_PROCESSOR,
+ Chunk(
+ Criterion(A_PROCESSOR_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MODEL, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MANUFACTURER, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_PROCESSOR_SPEED, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(
+ A_PROCESSOR_STEPPING,
+ StringComparator,
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(
+ A_PROCESSOR_FAMILLY,
+ StringComparator,
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(A_PROCESSOR_FAMILY_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_THREAD, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_CORE, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_SLOT,
+ Chunk(
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_STATUS, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SLOT_NAME, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_SOUND,
+ Chunk(
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SOUND_NAME, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_VIDEO,
+ Chunk(
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_QUANTITY, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VIDEO_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VIDEO_CHIPSET, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VIDEO_RESOLUTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MEMORY_CAPACITY, MemoryComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_NODE,
+ Chunk(
+ Criterion("OS", NodeOstypeComparator, NodeCriterionMatcherString(_.os.os.kernelName.wrap)),
+ Criterion(A_NODE_UUID, StringComparator, NodeCriterionMatcherString(_.id.value.wrap)),
+ Criterion(A_HOSTNAME, StringComparator, NodeCriterionMatcherString(_.fqdn.wrap)),
+ Criterion(A_OS_NAME, NodeOsNameComparator, NodeCriterionMatcherString(_.os.os.name.wrap)),
+ Criterion(A_OS_FULL_NAME, OrderedStringComparator, NodeCriterionMatcherString(_.os.fullName.wrap)),
+ Criterion(A_OS_VERSION, OrderedStringComparator, NodeCriterionMatcherString(_.os.version.value.wrap)),
+ Criterion(A_OS_SERVICE_PACK, OrderedStringComparator, NodeCriterionMatcherString(_.os.servicePack.toChunk)),
+ Criterion(A_OS_KERNEL_VERSION, OrderedStringComparator, NodeCriterionMatcherString(_.os.kernelVersion.value.wrap)),
+ Criterion(A_ARCH, StringComparator, NodeCriterionMatcherString(_.archDescription.toChunk)),
+ Criterion(A_STATE, NodeStateComparator, NodeCriterionMatcherString(_.rudderSettings.state.name.wrap)),
+ Criterion(A_OS_RAM, MemoryComparator, NodeCriterionMatcherLong(_.ram.map(_.size).toChunk)),
+ Criterion(A_OS_SWAP, MemoryComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_AGENTS_NAME, AgentComparator, AgentMatcher),
+ Criterion(A_ACCOUNT, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_LIST_OF_IP, NodeIpListComparator, NodeCriterionMatcherString(_.ipAddresses.map(_.inet))),
+ Criterion(A_ROOT_USER, StringComparator, NodeCriterionMatcherString(_.rudderAgent.user.wrap)),
+ Criterion(A_INVENTORY_DATE, DateComparator, NodeCriterionMatcherDate(_.lastInventoryDate.toChunk)),
+ Criterion(
+ A_POLICY_SERVER_UUID,
+ StringComparator,
+ NodeCriterionMatcherString(_.rudderSettings.policyServerId.value.wrap)
+ )
+ )
+ ),
+ ObjectCriterion(
+ OC_SOFTWARE,
+ Chunk(
+ Criterion(A_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_SOFT_VERSION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_EDITOR, EditorComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_LICENSE_EXP, DateComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_LICENSE_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_LICENSE_PRODUCT_ID, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_LICENSE_PRODUCT_KEY, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_NET_IF,
+ Chunk(
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_NETWORK_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(
+ A_NETIF_ADDRESS,
+ StringComparator,
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(A_NETIF_DHCP, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(
+ A_NETIF_GATEWAY,
+ StringComparator,
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(A_NETIF_MASK, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(
+ A_NETIF_SUBNET,
+ StringComparator,
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(A_NETIF_MAC, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_NETIF_TYPE, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_NETIF_TYPE_MIB, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ OC_FS,
+ Chunk(
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_NAME, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_MOUNT_POINT, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_FILE_COUNT, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_FREE_SPACE, MemoryComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_TOTAL_SPACE, MemoryComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ A_PROCESS,
+ Chunk(
+ Criterion("pid", JsonFixedKeyComparator(A_PROCESS, "pid", false), UnsupportedByNodeMinimalApi),
+ Criterion(
+ "commandName",
+ JsonFixedKeyComparator(A_PROCESS, "commandName", true),
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(
+ "cpuUsage",
+ JsonFixedKeyComparator(A_PROCESS, "cpuUsage", false),
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(
+ "memory",
+ JsonFixedKeyComparator(A_PROCESS, "memory", false),
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion("tty", JsonFixedKeyComparator(A_PROCESS, "tty", true), UnsupportedByNodeMinimalApi),
+ Criterion(
+ "virtualMemory",
+ JsonFixedKeyComparator(A_PROCESS, "virtualMemory", false),
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(
+ "started",
+ JsonFixedKeyComparator(A_PROCESS, "started", true),
+ UnsupportedByNodeMinimalApi
+ ),
+ Criterion(
+ "user",
+ JsonFixedKeyComparator(A_PROCESS, "user", true),
+ UnsupportedByNodeMinimalApi
+ )
+ )
+ ),
+ ObjectCriterion(
+ OC_VM_INFO,
+ Chunk(
+ Criterion(A_DESCRIPTION, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VM_TYPE, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VM_OWNER, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VM_STATUS, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VM_CPU, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VM_MEMORY, LongComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VM_ID, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VM_SUBSYSTEM, StringComparator, UnsupportedByNodeMinimalApi),
+ Criterion(A_VM_NAME, StringComparator, UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ A_EV,
+ Chunk(
+ Criterion("name.value", NameValueComparator(A_EV), UnsupportedByNodeMinimalApi)
+ )
+ ),
+ ObjectCriterion(
+ A_NODE_PROPERTY,
+ Chunk(
+ Criterion("name.value", NodePropertyComparator(A_NODE_PROPERTY), NodePropertiesMatcher)
+ )
+ ),
+ ObjectCriterion(
+ "group",
+ Chunk(
+ Criterion(
+ A_NODE_GROUP_UUID,
+ SubGroupComparator(groupRepo),
+ UnsupportedByNodeMinimalApi
+ )
+ )
+ )
+ )
+
+ val criteriaMap: SortedMap[String, ObjectCriterion] = SortedMap.from(criteria.map(c => (c.objectType, c)))
+}
+
+////// below, criterion matching logic /////.
+
+//////////////////////////////////////////////////////////////////////
+/////////////////// direct matching with NodeFact ///////////////////
+/////////////////////////////////////////////////////////////////////
+
+object MatcherUtils {
+ def getRegex(regexText: String): IOResult[Predicate[String]] = {
+ IOResult.attempt(
+ s"The regular expression '${regexText}' is not valid. Expected regex syntax is the java " +
+ s"one, documented here: http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html"
+ )(Pattern.compile(regexText, Pattern.DOTALL).asMatchPredicate())
+ }
+
+}
+
+final case class DebugInfo(comparatorName: String, value: Option[String]) {
+ def formatValue = value match {
+ case None => ""
+ case Some(v) => s" ${v}"
+ }
+ def debugMsg[A](values: Chunk[A], res: Boolean)(implicit serializer: A => String): String = {
+ s" [${res}] for '${comparatorName}${formatValue}' on [${values.map(serializer).mkString("|")}]"
+ }
+}
+
+final case class MatchHolderZio[A](debug: DebugInfo, values: Chunk[A], matcher: Chunk[A] => IOResult[Boolean])(implicit
+ serializer: A => String
+) {
+ def matches = for {
+ res <- matcher(values)
+ _ <- FactQueryProcessorLoggerPure.trace(debug.debugMsg(values, res))
+ } yield res
+}
+
+object MatchHolder {
+ def apply[A](debugInfo: DebugInfo, values: Chunk[A], matcher: Chunk[A] => Boolean)(implicit
+ serializer: A => String
+ ): MatchHolderZio[A] = {
+ MatchHolderZio[A](debugInfo, values, (vs: Chunk[A]) => matcher(vs).succeed)
+ }
+}
+
+trait NodeCriterionMatcher {
+ def matches(n: CoreNodeFact, comparator: CriterionComparator, value: String): IOResult[Boolean]
+}
+
+trait FullNodeCriterionMatcher {
+ def matches(n: NodeFact, comparator: CriterionComparator, value: String): IOResult[Boolean]
+}
+
+case class AlwaysFalse(reason: String) extends NodeCriterionMatcher {
+ override def matches(n: CoreNodeFact, comparator: CriterionComparator, value: String): IOResult[Boolean] = {
+ FactQueryProcessorLoggerPure.trace(s" [false] for AlwaysFalse: ${reason} ") *>
+ false.succeed
+ }
+}
+
+case object UnsupportedByNodeMinimalApi extends NodeCriterionMatcher {
+ override def matches(n: CoreNodeFact, comparator: CriterionComparator, value: String): IOResult[Boolean] = {
+ FactQueryProcessorLoggerPure.trace(
+ s" [false] ${comparator.id} is not supported by minimal node API, it must be handled by a special operator "
+ ) *>
+ false.succeed
+ }
+}
+
+/*
+ * A generic matcher that matches anything that has an order and can be parsed/serialized
+ * to one value. Typically, it can be String, numeric value, date, memories, etc.
+ */
+trait NodeCriterionOrderedValueMatcher[A] extends NodeCriterionMatcher {
+ def extractor: CoreNodeFact => Chunk[A]
+ def parseNum(value: String): Option[A]
+ def serialise(a: A): String
+ def order: Ordering[A]
+
+ def tryMatches(value: String, matches: A => MatchHolderZio[A]): IOResult[Boolean] = {
+ parseNum(value) match {
+ case Some(a) => matches(a).matches
+ case None =>
+ FactQueryProcessorLoggerPure.trace(s" - '${value}' can not be parsed into correct type: false'") *>
+ false.succeed
+ }
+ }
+
+ def matches(n: CoreNodeFact, comparator: CriterionComparator, value: String): IOResult[Boolean] = {
+ implicit val ser = serialise _
+
+ comparator match {
+ case Equals =>
+ tryMatches(value, a => MatchHolder[A](DebugInfo(Equals.id, Some(value)), extractor(n), _.exists(_ == a)))
+ case NotEquals =>
+ tryMatches(value, a => MatchHolder[A](DebugInfo(NotEquals.id, Some(value)), extractor(n), _.forall(_ != a)))
+ case Regex =>
+ for {
+ m <- MatcherUtils.getRegex(value)
+ r <- MatchHolder[A](
+ DebugInfo(Regex.id, Some(value)),
+ extractor(n),
+ _.exists(s => m.test(serialise(s)))
+ ).matches
+ } yield r
+ case NotRegex =>
+ for {
+ m <- MatcherUtils.getRegex(value)
+ r <- MatchHolder[A](
+ DebugInfo(NotRegex.id, Some(value)),
+ extractor(n),
+ _.forall(s => !m.test(serialise(s)))
+ ).matches
+ } yield r
+ case Exists =>
+ MatchHolder[A](DebugInfo(Exists.id, None), extractor(n), _.nonEmpty).matches
+ case NotExists =>
+ MatchHolder[A](DebugInfo(NotExists.id, None), extractor(n), _.isEmpty).matches
+ case Lesser =>
+ tryMatches(value, a => MatchHolder[A](DebugInfo(Lesser.id, Some(value)), extractor(n), _.exists(order.lt(_, a))))
+ case LesserEq =>
+ tryMatches(value, a => MatchHolder[A](DebugInfo(Lesser.id, Some(value)), extractor(n), _.exists(order.lteq(_, a))))
+ case Greater =>
+ tryMatches(value, a => MatchHolder[A](DebugInfo(Lesser.id, Some(value)), extractor(n), _.exists(order.gt(_, a))))
+ case GreaterEq =>
+ tryMatches(
+ value,
+ a => MatchHolder[A](DebugInfo(Lesser.id, Some(value)), extractor(n), _.exists(order.gteq(_, a)))
+ )
+ case c => MatchHolder[A](DebugInfo(s"unknown comparator: ${c}", Some(value)), Chunk(), _ => false).matches
+ }
+ }
+}
+
+final case class NodeCriterionMatcherString(extractor: CoreNodeFact => Chunk[String])
+ extends NodeCriterionOrderedValueMatcher[String] {
+ override def parseNum(value: String): Option[String] = Some(value)
+ override def serialise(a: String): String = a
+ val order = Ordering.String
+}
+
+final case class NodeCriterionMatcherInt(extractor: CoreNodeFact => Chunk[Int]) extends NodeCriterionOrderedValueMatcher[Int] {
+ override def parseNum(value: String): Option[Int] = try { Some(Integer.parseInt(value)) }
+ catch { case ex: NumberFormatException => None }
+ override def serialise(a: Int): String = a.toString
+ val order = Ordering.Int
+}
+
+final case class NodeCriterionMatcherLong(extractor: CoreNodeFact => Chunk[Long]) extends NodeCriterionOrderedValueMatcher[Long] {
+ override def parseNum(value: String): Option[Long] = try { Some(java.lang.Long.parseLong(value)) }
+ catch { case ex: NumberFormatException => None }
+ override def serialise(a: Long): String = a.toString
+ val order = Ordering.Long
+}
+
+final case class NodeCriterionMatcherFloat(extractor: CoreNodeFact => Chunk[Float])
+ extends NodeCriterionOrderedValueMatcher[Float] {
+ override def parseNum(value: String): Option[Float] = try { Some(java.lang.Float.parseFloat(value)) }
+ catch { case ex: NumberFormatException => None }
+ override def serialise(a: Float): String = a.toString
+ val order = Ordering.Float.TotalOrdering
+}
+
+final case class NodeCriterionMatcherDouble(extractor: CoreNodeFact => Chunk[Double])
+ extends NodeCriterionOrderedValueMatcher[Double] {
+ override def parseNum(value: String): Option[Double] = try { Some(java.lang.Double.parseDouble(value)) }
+ catch { case ex: NumberFormatException => None }
+ override def serialise(a: Double): String = a.toString
+ val order = Ordering.Double.TotalOrdering
+}
+
+final case class NodeCriterionMatcherDate(extractorNode: CoreNodeFact => Chunk[DateTime])
+ extends NodeCriterionOrderedValueMatcher[DateTime] {
+ val parseDate = (s: String) =>
+ DateFormaterService.parseDate(s).toOption.orElse(Try(DateTimeFormat.forPattern("dd/MM/YYYY").parseDateTime(s)).toOption)
+ // we need to accept both ISO format and old dd/MM/YYYY format for compatibility
+ // also, we discard the time, only keep date
+
+ override def extractor: CoreNodeFact => Chunk[DateTime] = (n: CoreNodeFact) => extractorNode(n).map(_.withTimeAtStartOfDay())
+ override def parseNum(value: String): Option[DateTime] = parseDate(value).map(_.withTimeAtStartOfDay())
+ override def serialise(a: DateTime): String = DateFormaterService.serialize(a)
+ val order = Ordering.by(_.getMillis)
+}
+
+/*
+ * Agent matcher is very special with some magic case like "any cfengine"
+ */
+object AgentMatcher extends NodeCriterionMatcher {
+ override def matches(n: CoreNodeFact, comparator: CriterionComparator, value: String): IOResult[Boolean] = {
+
+ implicit val serializer = (a: AgentType) => a.id
+ // this is magic: we equals on agent ID and in addition we have the magic value "cfengine" that matches
+ // any cfengine, which is just cfengine-community since we don't do anything else
+ def eq(value: String, tpe: AgentType): Boolean = {
+ value match {
+ case AgentComparator.ANY_CFENGINE => tpe == AgentType.CfeCommunity || tpe == AgentType.CfeEnterprise
+ case x => tpe.id == x || tpe.oldShortName == x
+ }
+ }
+
+ comparator match {
+ case Equals =>
+ MatchHolder[AgentType](DebugInfo(Equals.id, Some(value)), Chunk(n.rudderAgent.agentType), _.exists(eq(value, _))).matches
+ case NotEquals => matches(n, Equals, value).map(!_)
+ case c => MatchHolder[AgentType](DebugInfo(s"unknown comparator: ${c}", Some(value)), Chunk(), _ => false).matches
+ }
+ }
+}
+
+/*
+ * A generic matcher for (key, value) elements.
+ * Key are string, values are string or json.
+ * It will allow to match on:
+ * - key=value
+ * - hasKey
+ * - jsonSelect
+ * In addition to eq/exist/regex
+ * Regex follows a
+ *
+ * The serialise method is used for the debug representation and k=v matcher.
+ * The getValue method is for the value part, used in jsonSelect.
+ */
+trait NodeCriterionKeyValueMatcher[A] extends NodeCriterionMatcher {
+ def extractor: CoreNodeFact => Chunk[A]
+ def serialise(a: A): String
+ def getKey(a: A): String
+ def getValue(a: A): String
+ // ordering on key, alternative could be done on values
+ def order: Ordering[String] = Ordering.String
+
+ def matches(n: CoreNodeFact, comparator: CriterionComparator, value: String): IOResult[Boolean] = {
+ implicit val ser = serialise _
+
+ // for Key/Value comparator, we have an expected format for the value for some operator:
+ // Equals and NotEquals: value is: ${key}=${value}, ie "=" is mandatory
+ // HasKey: value is: ${key}:${value} , ie ":" is mandatory
+ def getKVEquals(value: String): IOResult[SplittedValue] = {
+ val kv = NodePropertyMatcherUtils.splitInput(value, "=")
+ kv.values match {
+ case Nil =>
+ Inconsistency(
+ s"When looking for 'key=value', the '=' is mandatory. The left part is a key name, and the right part is the string to look for."
+ ).fail
+ case _ => kv.succeed
+ }
+ }
+
+ def getKVHasKey(value: String): IOResult[SplittedValue] = {
+ val kv = NodePropertyMatcherUtils.splitInput(value, ":")
+ kv.values match {
+ case Nil =>
+ Inconsistency(
+ s"When looking for 'key:json path expression', we found zero ':', but at least one is mandatory. The left " +
+ "part is a key name, and the right part is the JSON path expression (see https://github.com/json-path/JsonPath). " +
+ "For example: datacenter:world.europe.[?(@.city=='Paris')]"
+ ).fail
+ case _ => kv.succeed
+ }
+ }
+
+ comparator match {
+ case Equals =>
+ for {
+ kv <- getKVEquals(value)
+ res <- MatchHolder[A](
+ DebugInfo(Equals.id, Some(value)),
+ extractor(n),
+ _.exists(v => getKey(v) == kv.key && getValue(v) == kv.value)
+ ).matches
+ } yield res
+
+ // not equals look for a value not equal to parameter but only for the given key
+ // not having the key is also not equals
+ case NotEquals =>
+ for {
+ kv <- getKVEquals(value)
+ res <- MatchHolder[A](
+ DebugInfo(Equals.id, Some(value)),
+ extractor(n),
+ _.forall(v => getKey(v) != kv.key || (getKey(v) == kv.key && getValue(v) != kv.value))
+ ).matches
+ } yield res
+
+ case Regex =>
+ for {
+ m <- MatcherUtils.getRegex(value)
+ res <- MatchHolder[A](
+ DebugInfo(Regex.id, Some(value)),
+ extractor(n),
+ _.exists(s => m.test(serialise(s)))
+ ).matches
+ } yield res
+ case NotRegex =>
+ for {
+ m <- MatcherUtils.getRegex(value)
+ res <- MatchHolder[A](
+ DebugInfo(NotRegex.id, Some(value)),
+ extractor(n),
+ _.forall(s => !m.test(serialise(s)))
+ ).matches
+ } yield res
+ case Exists =>
+ MatchHolder[A](DebugInfo(Exists.id, None), extractor(n), _.nonEmpty).matches
+ case NotExists =>
+ MatchHolder[A](DebugInfo(NotExists.id, None), extractor(n), _.isEmpty).matches
+ case Lesser =>
+ MatchHolder[A](DebugInfo(Lesser.id, Some(value)), extractor(n), _.exists(a => order.lt(getKey(a), value))).matches
+ case LesserEq =>
+ MatchHolder[A](DebugInfo(Lesser.id, Some(value)), extractor(n), _.exists(a => order.lteq(getKey(a), value))).matches
+ case Greater =>
+ MatchHolder[A](DebugInfo(Lesser.id, Some(value)), extractor(n), _.exists(a => order.gt(getKey(a), value))).matches
+ case GreaterEq =>
+ MatchHolder[A](DebugInfo(Lesser.id, Some(value)), extractor(n), _.exists(a => order.gteq(getKey(a), value))).matches
+
+ case HasKey =>
+ MatchHolder[A](DebugInfo(HasKey.id, Some(value)), extractor(n), _.exists(kv => getKey(kv) == value)).matches
+ case KVC.JsonSelect =>
+ // for JSON select: error in json path matching (ie "JsonSelect.exists") are considered as not matching, not as errors
+ for {
+ kv <- getKVHasKey(value)
+ path <- JsonSelect.compilePath(kv.value).toIO
+ matcher = (as: Chunk[A]) => {
+ ZIO.exists(as)(a => {
+ ZIO
+ .when(getKey(a) == kv.key)(JsonSelect.exists(path, getValue(a)).toIO)
+ .map(_.getOrElse(false))
+ .catchAll(_ => false.succeed)
+ })
+ }
+ res <- MatchHolderZio[A](DebugInfo(KVC.JsonSelect.id, Some(value)), extractor(n), matcher).matches
+ } yield res
+
+ case c => MatchHolder[A](DebugInfo(s"unknown comparator: ${c}", Some(value)), Chunk(), _ => false).matches
+ }
+ }
+}
+
+//object EnvironmentVariableMatcher extends NodeCriterionKeyValueMatcher[(String, String)] {
+// override def extractor: NodeFact => Chunk[(String, String)] = { (n: NodeFact) => n.environmentVariables }
+// override def serialise(a: (String, String)): String = s"""${a._1}=${a._2}"""
+// override def getKey(a: (String, String)): String = a._1
+// override def getValue(a: (String, String)): String = a._2
+//}
+
+object NodePropertiesMatcher extends NodeCriterionKeyValueMatcher[NodeProperty] {
+ /*
+ * Node properties search are done on both node properties and inventory custom properties
+ */
+ override def extractor: CoreNodeFact => Chunk[NodeProperty] = { (n: CoreNodeFact) => n.properties }
+ override def serialise(a: NodeProperty): String = s"""${a.name}=${a.valueAsString}"""
+ override def getKey(a: NodeProperty): String = a.name
+ override def getValue(a: NodeProperty): String = a.valueAsString
+}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFact.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFact.scala
index a55953f74c8..0f011aedb94 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFact.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFact.scala
@@ -37,8 +37,12 @@
package com.normation.rudder.facts.nodes
+import com.normation.eventlog.EventActor
+import com.normation.eventlog.ModificationId
import com.normation.inventory.domain._
import com.normation.inventory.domain.{Version => SVersion}
+import com.normation.rudder.apidata.NodeDetailLevel
+import com.normation.rudder.domain.eventlog
import com.normation.rudder.domain.nodes.MachineInfo
import com.normation.rudder.domain.nodes.Node
import com.normation.rudder.domain.nodes.NodeInfo
@@ -157,7 +161,7 @@ final case class SoftwareFact(
object SoftwareFact {
implicit class ToSoftware(sf: SoftwareFact) {
def toSoftware: Software = Software(
- SoftwareUuid(sf.name),
+ SoftwareUuid(""), // here, we don't know the uuid. We need a way to mark that it's not valid and don't risk using a bad one
Some(sf.name),
None,
Some(sf.version),
@@ -168,13 +172,18 @@ object SoftwareFact {
sf.sourceVersion
)
}
+
+ def fromSoftware(s: Software): Option[SoftwareFact] = {
+ import NodeFact._
+ s.toFact
+ }
}
-object NodeFact {
+object MinimalNodeFactInterface {
/*
* Check if the node fact are the same.
- * Sameness is not equatity. It does not look for:
+ * Sameness is not equality. It does not look for:
* - inventory date
* - fact processing time
* - acceptation time
@@ -184,17 +193,15 @@ object NodeFact {
* most node fact are never compared for sameness.
* Same is heavy, don't use it often !
*/
- def same(a: NodeFact, b: NodeFact): Boolean = {
- // compare two chunk sameness
- def compare[A, B: Ordering](accessor: NodeFact => Chunk[A])(orderOn: A => B): Boolean = {
+ def same(a: MinimalNodeFactInterface, b: MinimalNodeFactInterface): Boolean = {
+ def eq[A](accessor: MinimalNodeFactInterface => A): Boolean = {
+ accessor(a) == accessor(b)
+ }
+ def compare[A, B: Ordering](accessor: MinimalNodeFactInterface => Chunk[A])(orderOn: A => B): Boolean = {
accessor(a).size == accessor(b).size &&
accessor(a).sortBy(orderOn) == accessor(b).sortBy(orderOn)
}
- def eq[A](accessor: NodeFact => A): Boolean = {
- accessor(a) == accessor(b)
- }
-
eq(_.id) &&
eq(_.description) &&
eq(_.fqdn) &&
@@ -204,7 +211,109 @@ object NodeFact {
eq(_.rudderAgent) &&
compare(_.properties)(_.name) &&
compare(_.ipAddresses)(_.inet) &&
- eq(_.timezone) &&
+ eq(_.timezone)
+ }
+
+ def isSystem(node: MinimalNodeFactInterface): Boolean = {
+ node.rudderSettings.kind != NodeKind.Node
+ }
+
+ def ipAddresses(node: MinimalNodeFactInterface): List[String] = {
+ node.ipAddresses.map(_.inet).toList
+ }
+
+ def toNode(node: MinimalNodeFactInterface): Node = Node(
+ node.id,
+ node.fqdn,
+ "", // description
+ node.rudderSettings.state,
+ isSystem(node),
+ isSystem((node)),
+ node.creationDate,
+ node.rudderSettings.reportingConfiguration,
+ node.properties.toList,
+ node.rudderSettings.policyMode
+ )
+
+ def toNodeInfo(node: MinimalNodeFactInterface, ram: Option[MemorySize], archDescription: Option[String]): NodeInfo = NodeInfo(
+ toNode(node),
+ node.fqdn,
+ Some(node.machine),
+ node.os,
+ node.ipAddresses.toList.map(_.inet),
+ node.lastInventoryDate.getOrElse(node.factProcessedDate),
+ node.rudderSettings.keyStatus,
+ Chunk(
+ AgentInfo(
+ node.rudderAgent.agentType,
+ Some(node.rudderAgent.version),
+ node.rudderAgent.securityToken,
+ node.rudderAgent.capabilities.toSet
+ )
+ ),
+ node.rudderSettings.policyServerId,
+ node.rudderAgent.user,
+ archDescription,
+ ram,
+ node.timezone
+ )
+
+ def toSrv(node: MinimalNodeFactInterface): Srv = {
+ Srv(
+ node.id,
+ node.rudderSettings.status,
+ node.fqdn,
+ node.os.os.kernelName,
+ node.os.os.name,
+ node.os.fullName,
+ ipAddresses(node),
+ node.creationDate,
+ isSystem(node)
+ )
+ }
+
+ def toNodeSummary(node: MinimalNodeFactInterface): NodeSummary = {
+ NodeSummary(
+ node.id,
+ node.rudderSettings.status,
+ node.rudderAgent.user,
+ node.fqdn,
+ node.os,
+ node.rudderSettings.policyServerId,
+ node.rudderSettings.keyStatus
+ )
+ }
+}
+
+object NodeFact {
+
+ /*
+ * Check if the node fact are the same.
+ * Sameness is not equatity. It does not look for:
+ * - inventory date
+ * - fact processing time
+ * - acceptation time
+ * - order in any collection
+ *
+ * Having a hashcode on node fact would be inefficient,
+ * most node fact are never compared for sameness.
+ * Same is heavy, don't use it often !
+ */
+ def same(nfa: NodeFact, nfb: NodeFact)(implicit attrs: SelectFacts): Boolean = {
+
+ val a = SelectFacts.mask(nfa)
+ val b = SelectFacts.mask(nfb)
+
+ def eq[A](accessor: NodeFact => A): Boolean = {
+ accessor(a) == accessor(b)
+ }
+ // compare two chunk sameness
+ def compare[A, B: Ordering](accessor: NodeFact => Chunk[A])(orderOn: A => B): Boolean = {
+ accessor(a).size == accessor(b).size &&
+ accessor(a).sortBy(orderOn) == accessor(b).sortBy(orderOn)
+ }
+
+ MinimalNodeFactInterface.same(a, b) &&
eq(_.ram) &&
eq(_.swap) &&
eq(_.archDescription) &&
@@ -282,7 +391,7 @@ object NodeFact {
.using(_.sortBy(_.name))
}
- def toMachineId(nodeId: NodeId) = MachineUuid("machine-" + nodeId.value)
+ def toMachineId(nodeId: NodeId) = MachineUuid("machine-for-" + nodeId.value)
implicit class IterableToChunk[A](it: Iterable[A]) {
def toChunk: Chunk[A] = Chunk.fromIterable(it)
@@ -330,68 +439,49 @@ object NodeFact {
}
implicit class ToCompat(node: NodeFact) {
+ def mask[A](s: SelectFactConfig[A]) = {
+ s.mode match {
+ case SelectMode.Retrieve => node
+ case SelectMode.Ignore => s.modify.setTo(s.zero)(node)
+ }
+ }
- def toNode: Node = Node(
- node.id,
- node.fqdn,
- "", // description
- node.rudderSettings.state,
- node.isSystem,
- node.isPolicyServer,
- node.creationDate,
- node.rudderSettings.reportingConfiguration,
- node.properties.toList,
- node.rudderSettings.policyMode
- )
+ def maskWith(attrs: SelectFacts): NodeFact = {
+ node
+ .mask(attrs.swap)
+ .mask(attrs.accounts)
+ .mask(attrs.bios)
+ .mask(attrs.controllers)
+ .mask(attrs.environmentVariables)
+ .mask(attrs.inputs)
+ .mask(attrs.fileSystems)
+ .mask(attrs.localGroups)
+ .mask(attrs.localUsers)
+ .mask(attrs.logicalVolumes)
+ .mask(attrs.memories)
+ .mask(attrs.networks)
+ .mask(attrs.ports)
+ .mask(attrs.physicalVolumes)
+ .mask(attrs.processes)
+ .mask(attrs.processors)
+ .mask(attrs.slots)
+ .mask(attrs.software)
+ .mask(attrs.softwareUpdate)
+ .mask(attrs.sounds)
+ .mask(attrs.storages)
+ .mask(attrs.videos)
+ .mask(attrs.vms)
+ }
- def toNodeInfo: NodeInfo = NodeInfo(
- node.toNode,
- node.fqdn,
- Some(node.machine),
- node.os,
- node.ipAddresses.toList.map(_.inet),
- node.lastInventoryDate.getOrElse(node.factProcessedDate),
- node.rudderSettings.keyStatus,
- Chunk(
- AgentInfo(
- node.rudderAgent.agentType,
- Some(node.rudderAgent.version),
- node.rudderAgent.securityToken,
- node.rudderAgent.capabilities.toSet
- )
- ),
- node.rudderSettings.policyServerId,
- node.rudderAgent.user,
- node.archDescription,
- node.ram,
- node.timezone
- )
+ def toCore: CoreNodeFact = CoreNodeFact.fromMininal(node)
- def toSrv: Srv = {
- Srv(
- node.id,
- node.rudderSettings.status,
- node.fqdn,
- node.os.os.kernelName,
- node.os.os.name,
- node.os.fullName,
- node.serverIps,
- node.creationDate,
- node.isPolicyServer
- )
- }
+ def toNode: Node = MinimalNodeFactInterface.toNode(node)
- def toNodeSummary: NodeSummary = {
- NodeSummary(
- node.id,
- node.rudderSettings.status,
- node.rudderAgent.user,
- node.fqdn,
- node.os,
- node.rudderSettings.policyServerId,
- node.rudderSettings.keyStatus
- )
- }
+ def toNodeInfo: NodeInfo = MinimalNodeFactInterface.toNodeInfo(node, node.ram, node.archDescription)
+
+ def toSrv: Srv = MinimalNodeFactInterface.toSrv(node)
+
+ def toNodeSummary: NodeSummary = MinimalNodeFactInterface.toNodeSummary(node)
def toNodeInventory: NodeInventory = NodeInventory(
node.toNodeSummary,
@@ -456,7 +546,12 @@ object NodeFact {
},
nodeInfo.hostname,
nodeInfo.osDetails,
- nodeInfo.machine.getOrElse(MachineInfo(NodeFact.toMachineId(nodeInfo.id), UnknownMachineType, None, None)),
+ nodeInfo.machine.getOrElse(inventory match {
+ case Right(FullInventory(_, Some(m))) =>
+ MachineInfo(m.id, m.machineType, m.systemSerialNumber, m.manufacturer)
+ case _ => // in that case, we just don't have any info on the matchine, derive a false id from node id
+ MachineInfo(NodeFact.toMachineId(nodeInfo.id), UnknownMachineType, None, None)
+ }),
RudderSettings(
nodeInfo.keyStatus,
nodeInfo.nodeReportingConfiguration,
@@ -480,9 +575,9 @@ object NodeFact {
Some(nodeInfo.inventoryDate),
nodeInfo.ips.map(IpAddress(_)).toChunk,
nodeInfo.timezone,
+ nodeInfo.archDescription,
nodeInfo.ram,
inventory.toOption.flatMap(_.node.swap),
- nodeInfo.archDescription,
inventory.toChunk.flatMap(_.node.accounts),
inventory.toChunk.flatMap(_.machine.chunk(_.bios)),
inventory.toChunk.flatMap(_.machine.chunk(_.controllers)),
@@ -538,7 +633,8 @@ object NodeFact {
inventory.node.main.hostname,
inventory.node.main.osDetails,
MachineInfo(
- NodeFact.toMachineId(inventory.node.main.id),
+ // we should have the machine in a new full inventory, else generate an uuid for the unknown one
+ inventory.machine.map(_.id).getOrElse(NodeFact.toMachineId(inventory.node.main.id)),
inventory.machine.map(_.machineType).getOrElse(UnknownMachineType),
inventory.machine.flatMap(_.systemSerialNumber),
inventory.machine.flatMap(_.manufacturer)
@@ -556,6 +652,30 @@ object NodeFact {
newFromFullInventory(FullInventory(inventory.node, Some(inventory.machine)), Some(inventory.applications))
}
+ def fromMinimal(a: MinimalNodeFactInterface): NodeFact = {
+ a match {
+ case x: NodeFact => x
+ case _ =>
+ NodeFact(
+ a.id,
+ a.description,
+ a.fqdn,
+ a.os,
+ a.machine,
+ a.rudderSettings,
+ a.rudderAgent,
+ a.properties,
+ a.creationDate,
+ a.factProcessedDate,
+ a.lastInventoryDate,
+ a.ipAddresses,
+ a.timezone,
+ a.archDescription,
+ a.ram
+ )
+ }
+ }
+
/*
* Update all inventory parts from that node fact.
* The inventory parts are overridden, there is no merge
@@ -565,6 +685,14 @@ object NodeFact {
updateFullInventory(node, FullInventory(inventory.node, Some(inventory.machine)), Some(inventory.applications))
}
+ def updateInventory(core: CoreNodeFact, inventory: Inventory): NodeFact = {
+ updateFullInventory(
+ NodeFact.fromMinimal(core),
+ FullInventory(inventory.node, Some(inventory.machine)),
+ Some(inventory.applications)
+ )
+ }
+
// FullInventory does keep the software, but only their IDs, which is not a concept we still have.
// So the caller can say "I don't know what software" with a None, or "there's no software" with a Some(Nil)
// Also, we don't update status here, use move or similar methods to change node status.
@@ -592,7 +720,7 @@ object NodeFact {
// now machine are mandatory so if we don't have it inventory, don't update
val machine = inventory.machine.map { m =>
MachineInfo(
- NodeFact.toMachineId(inventory.node.main.id),
+ m.id,
m.machineType,
m.systemSerialNumber,
m.manufacturer
@@ -686,6 +814,357 @@ object NodeFact {
}
+trait MinimalNodeFactInterface {
+ def id: NodeId
+ def description: Option[String]
+ def fqdn: String
+ def os: OsDetails
+ def machine: MachineInfo
+ def rudderSettings: RudderSettings
+ def rudderAgent: RudderAgent
+ def properties: Chunk[NodeProperty]
+ def creationDate: DateTime
+ def factProcessedDate: DateTime
+ def lastInventoryDate: Option[DateTime]
+ def ipAddresses: Chunk[IpAddress]
+ def timezone: Option[NodeTimezone]
+ def archDescription: Option[String]
+ def ram: Option[MemorySize]
+}
+
+/*
+ * Subset of commonly used properties of node fact, same as NodeInfo.
+ * It should have exactly the same fields as MinimalNodeFactInterface
+ */
+final case class CoreNodeFact(
+ id: NodeId,
+ description: Option[String],
+ @jsonField("hostname")
+ fqdn: String,
+ os: OsDetails,
+ machine: MachineInfo,
+ rudderSettings: RudderSettings,
+ rudderAgent: RudderAgent,
+ properties: Chunk[NodeProperty],
+ creationDate: DateTime,
+ factProcessedDate: DateTime,
+ lastInventoryDate: Option[DateTime] = None,
+ ipAddresses: Chunk[IpAddress] = Chunk.empty,
+ timezone: Option[NodeTimezone] = None,
+ archDescription: Option[String] = None,
+ ram: Option[MemorySize] = None
+) extends MinimalNodeFactInterface
+
+object CoreNodeFact {
+
+ def updateNode(node: CoreNodeFact, n: Node): CoreNodeFact = {
+ import com.softwaremill.quicklens._
+ node
+ .modify(_.description)
+ .setTo(Some(n.description))
+ .modify(_.rudderSettings.state)
+ .setTo(n.state)
+ .modify(_.rudderSettings.kind)
+ .setTo(if (n.isPolicyServer) NodeKind.Relay else NodeKind.Node)
+ .modify(_.creationDate)
+ .setTo(n.creationDate)
+ .modify(_.rudderSettings.reportingConfiguration)
+ .setTo(n.nodeReportingConfiguration)
+ .modify(_.properties)
+ .setTo(Chunk.fromIterable(n.properties))
+ .modify(_.rudderSettings.policyMode)
+ .setTo(n.policyMode)
+ }
+
+ def fromMininal(a: MinimalNodeFactInterface): CoreNodeFact = {
+ a match {
+ case c: CoreNodeFact => c
+ case _ =>
+ CoreNodeFact(
+ a.id,
+ a.description,
+ a.fqdn,
+ a.os,
+ a.machine,
+ a.rudderSettings,
+ a.rudderAgent,
+ a.properties,
+ a.creationDate,
+ a.factProcessedDate,
+ a.lastInventoryDate,
+ a.ipAddresses,
+ a.timezone,
+ a.archDescription,
+ a.ram
+ )
+ }
+ }
+
+ def same(a: CoreNodeFact, b: CoreNodeFact): Boolean = {
+ MinimalNodeFactInterface.same(a, b) &&
+ a.archDescription == b.archDescription &&
+ a.ram == b.ram
+ }
+
+ implicit class ToCompat(node: CoreNodeFact) {
+
+ def toNode: Node = MinimalNodeFactInterface.toNode(node)
+
+ def toNodeInfo: NodeInfo = MinimalNodeFactInterface.toNodeInfo(node, node.ram, node.archDescription)
+
+ def toSrv: Srv = MinimalNodeFactInterface.toSrv(node)
+
+ def toNodeSummary: NodeSummary = MinimalNodeFactInterface.toNodeSummary(node)
+ }
+}
+
+sealed trait SelectMode
+object SelectMode {
+ case object Ignore extends SelectMode
+ case object Retrieve extends SelectMode
+}
+
+case class SelectFactConfig[A](
+ mode: SelectMode,
+ selector: NodeFact => A,
+ modify: PathLazyModify[NodeFact, A],
+ zero: A
+) {
+ // copy helper for fluent api
+ def toIgnore: SelectFactConfig[A] = this.copy(mode = SelectMode.Ignore)
+ def toRetrieve: SelectFactConfig[A] = this.copy(mode = SelectMode.Retrieve)
+ def invertMode: SelectFactConfig[A] = if (this.mode == SelectMode.Ignore) toRetrieve else toIgnore
+
+ override def toString = this.mode.toString
+}
+
+case class SelectFacts(
+ swap: SelectFactConfig[Option[MemorySize]],
+ accounts: SelectFactConfig[Chunk[String]],
+ bios: SelectFactConfig[Chunk[Bios]],
+ controllers: SelectFactConfig[Chunk[Controller]],
+ environmentVariables: SelectFactConfig[Chunk[(String, String)]],
+ fileSystems: SelectFactConfig[Chunk[FileSystem]],
+ inputs: SelectFactConfig[Chunk[InputDevice]],
+ localGroups: SelectFactConfig[Chunk[LocalGroup]],
+ localUsers: SelectFactConfig[Chunk[LocalUser]],
+ logicalVolumes: SelectFactConfig[Chunk[LogicalVolume]],
+ memories: SelectFactConfig[Chunk[MemorySlot]],
+ networks: SelectFactConfig[Chunk[Network]],
+ physicalVolumes: SelectFactConfig[Chunk[PhysicalVolume]],
+ ports: SelectFactConfig[Chunk[Port]],
+ processes: SelectFactConfig[Chunk[Process]],
+ processors: SelectFactConfig[Chunk[Processor]],
+ slots: SelectFactConfig[Chunk[Slot]],
+ software: SelectFactConfig[Chunk[SoftwareFact]],
+ softwareUpdate: SelectFactConfig[Chunk[SoftwareUpdate]],
+ sounds: SelectFactConfig[Chunk[Sound]],
+ storages: SelectFactConfig[Chunk[Storage]],
+ videos: SelectFactConfig[Chunk[Video]],
+ vms: SelectFactConfig[Chunk[VirtualMachine]]
+) {
+ def debugString =
+ this.productElementNames.zip(this.productIterator).map { case (a, b) => s"${a}: ${b.toString}" }.mkString(", ")
+}
+
+sealed trait SelectNodeStatus { def name: String }
+object SelectNodeStatus {
+ object Pending extends SelectNodeStatus { val name = PendingInventory.name }
+ object Accepted extends SelectNodeStatus { val name = AcceptedInventory.name }
+ object Any extends SelectNodeStatus { val name = "any" }
+}
+
+object SelectFacts {
+
+ implicit class Invert(c: SelectFacts) {
+ def invert: SelectFacts = {
+ SelectFacts(
+ c.swap.invertMode,
+ c.accounts.invertMode,
+ c.bios.invertMode,
+ c.controllers.invertMode,
+ c.environmentVariables.invertMode,
+ c.fileSystems.invertMode,
+ c.inputs.invertMode,
+ c.localGroups.invertMode,
+ c.localUsers.invertMode,
+ c.logicalVolumes.invertMode,
+ c.memories.invertMode,
+ c.networks.invertMode,
+ c.physicalVolumes.invertMode,
+ c.ports.invertMode,
+ c.processes.invertMode,
+ c.processors.invertMode,
+ c.slots.invertMode,
+ c.software.invertMode,
+ c.softwareUpdate.invertMode,
+ c.sounds.invertMode,
+ c.storages.invertMode,
+ c.videos.invertMode,
+ c.vms.invertMode
+ )
+ }
+ }
+
+ // format: off
+ // there's perhaps a better way to do that, but `shrug` don't know about it
+ val none = SelectFacts(
+ SelectFactConfig(SelectMode.Ignore,_.swap, modifyLens[NodeFact](_.swap), None),
+ SelectFactConfig(SelectMode.Ignore,_.accounts, modifyLens[NodeFact](_.accounts), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.bios, modifyLens[NodeFact](_.bios), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.controllers, modifyLens[NodeFact](_.controllers), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.environmentVariables, modifyLens[NodeFact](_.environmentVariables), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.fileSystems, modifyLens[NodeFact](_.fileSystems), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.inputs, modifyLens[NodeFact](_.inputs), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.localGroups, modifyLens[NodeFact](_.localGroups), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.localUsers, modifyLens[NodeFact](_.localUsers), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.logicalVolumes, modifyLens[NodeFact](_.logicalVolumes), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.memories, modifyLens[NodeFact](_.memories), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.networks, modifyLens[NodeFact](_.networks), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.physicalVolumes, modifyLens[NodeFact](_.physicalVolumes), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.ports, modifyLens[NodeFact](_.ports), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.processes, modifyLens[NodeFact](_.processes), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.processors, modifyLens[NodeFact](_.processors), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.slots, modifyLens[NodeFact](_.slots), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.software, modifyLens[NodeFact](_.software), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.softwareUpdate, modifyLens[NodeFact](_.softwareUpdate), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.sounds, modifyLens[NodeFact](_.sounds), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.storages, modifyLens[NodeFact](_.storages), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.videos, modifyLens[NodeFact](_.videos), Chunk.empty),
+ SelectFactConfig(SelectMode.Ignore,_.vms, modifyLens[NodeFact](_.vms), Chunk.empty)
+ )
+
+ val all = SelectFacts(
+ none.swap.toRetrieve,
+ none.accounts.toRetrieve,
+ none.bios.toRetrieve,
+ none.controllers.toRetrieve,
+ none.environmentVariables.toRetrieve,
+ none.fileSystems.toRetrieve,
+ none.inputs.toRetrieve,
+ none.localGroups.toRetrieve,
+ none.localUsers.toRetrieve,
+ none.logicalVolumes.toRetrieve,
+ none.memories.toRetrieve,
+ none.networks.toRetrieve,
+ none.physicalVolumes.toRetrieve,
+ none.ports.toRetrieve,
+ none.processes.toRetrieve,
+ none.processors.toRetrieve,
+ none.slots.toRetrieve,
+ none.software.toRetrieve,
+ none.softwareUpdate.toRetrieve,
+ none.sounds.toRetrieve,
+ none.storages.toRetrieve,
+ none.videos.toRetrieve,
+ none.vms.toRetrieve
+ )
+ // format: on
+
+ val softwareOnly = none.copy(software = none.software.toRetrieve)
+ val noSoftware = all.copy(software = all.software.toIgnore)
+ val default = all.copy(processes = all.processes.toIgnore, software = all.software.toIgnore)
+
+ // inventory elements, not carring for software
+ def retrieveInventory(attrs: SelectFacts): Boolean = {
+ !(attrs.copy(software = SelectFacts.none.software) == SelectFacts.none)
+ }
+
+ def fromNodeDetailLevel(level: NodeDetailLevel): SelectFacts = {
+ // change from none to get
+ def toGet[A](s: SelectFactConfig[A], switch: Boolean): SelectFactConfig[A] = {
+ if (switch) s.toRetrieve
+ else s
+ }
+ SelectFacts(
+ toGet(none.swap, level.fields.contains("fileSystems")),
+ toGet(none.accounts, level.fields.contains("accounts")),
+ toGet(none.bios, level.fields.contains("bios")),
+ toGet(none.controllers, level.fields.contains("controllers")),
+ toGet(none.environmentVariables, level.fields.contains("environmentVariables")),
+ toGet(none.fileSystems, level.fields.contains("fileSystems")),
+ none.inputs,
+ none.localGroups,
+ none.localUsers,
+ none.logicalVolumes,
+ toGet(none.memories, level.fields.contains("memories")),
+ toGet(none.networks, level.fields.contains("networkInterfaces")),
+ none.physicalVolumes,
+ toGet(none.ports, level.fields.contains("ports")),
+ toGet(none.processes, level.fields.contains("processes")),
+ toGet(none.processors, level.fields.contains("processors")),
+ toGet(none.slots, level.fields.contains("slots")),
+ toGet(none.software, level.fields.contains("software")),
+ toGet(none.softwareUpdate, level.fields.contains("softwareUpdate")),
+ toGet(none.sounds, level.fields.contains("sound")),
+ toGet(none.storages, level.fields.contains("storage")),
+ toGet(none.videos, level.fields.contains("videos")),
+ toGet(none.vms, level.fields.contains("virtualMachines"))
+ )
+ }
+
+ // semantic: having a new node fact, keep old fact info if the select mode says "ignore"
+ // and keep new fact if it says "retrieve"
+ def merge(newFact: NodeFact, existing: Option[NodeFact])(implicit attrs: SelectFacts): NodeFact = {
+ implicit class NodeFactMerge(newFact: NodeFact) {
+
+ // keep newFact
+ def update[A](config: SelectFactConfig[A])(implicit oldFact: NodeFact) = {
+ config.mode match {
+ case SelectMode.Retrieve => // keep new fact
+ newFact
+ case SelectMode.Ignore => // get info from old fact
+ config.modify.setTo(config.selector(oldFact))(newFact)
+ }
+ }
+ }
+
+ existing match {
+ case None => newFact
+ // we assume that all the properties not in SelectFacts are up-to-date, so we start with newFact and only update
+ case Some(of) =>
+ implicit val oldFact = of
+
+ newFact
+ .update(attrs.swap)
+ .update(attrs.accounts)
+ .update(attrs.bios)
+ .update(attrs.controllers)
+ .update(attrs.environmentVariables)
+ .update(attrs.inputs)
+ .update(attrs.fileSystems)
+ .update(attrs.localGroups)
+ .update(attrs.localUsers)
+ .update(attrs.logicalVolumes)
+ .update(attrs.memories)
+ .update(attrs.networks)
+ .update(attrs.ports)
+ .update(attrs.physicalVolumes)
+ .update(attrs.processes)
+ .update(attrs.processors)
+ .update(attrs.slots)
+ .update(attrs.software)
+ .update(attrs.softwareUpdate)
+ .update(attrs.sounds)
+ .update(attrs.storages)
+ .update(attrs.videos)
+ .update(attrs.vms)
+ }
+ }
+
+ // given a core node fact, add attributes from an other fact based on what attrs says
+ def mergeCore(cnf: CoreNodeFact, fact: NodeFact)(implicit attrs: SelectFacts): NodeFact = {
+ // from a implementation point of view, it's the opposite of merge WRT SelectFacts
+ merge(NodeFact.fromMinimal(cnf), Some(fact))(attrs.invert)
+ }
+
+ // mask the given NodeFact to only expose attrs that are in "Retrieve"
+ def mask(fact: NodeFact)(implicit attrs: SelectFacts): NodeFact = {
+ // masking is merging with the input fact as "old" and a version with everything set to empty
+ mergeCore(CoreNodeFact.fromMininal(fact), fact)
+ }
+}
+
final case class NodeFact(
id: NodeId,
description: Option[String],
@@ -712,9 +1191,9 @@ final case class NodeFact(
// inventory details, optional
+ archDescription: Option[String] = None,
ram: Option[MemorySize] = None,
swap: Option[MemorySize] = None,
- archDescription: Option[String] = None,
accounts: Chunk[String] = Chunk.empty,
bios: Chunk[Bios] = Chunk.empty,
controllers: Chunk[Controller] = Chunk.empty,
@@ -737,9 +1216,8 @@ final case class NodeFact(
storages: Chunk[Storage] = Chunk.empty,
videos: Chunk[Video] = Chunk.empty,
vms: Chunk[VirtualMachine] = Chunk.empty
-) {
- // we don't have a machine id anymore, by convention it's the node id prefixed by "machine-"
- def machineId = NodeFact.toMachineId(id)
+) extends MinimalNodeFactInterface {
+ def machineId = machine.id
def isPolicyServer: Boolean = rudderSettings.kind != NodeKind.Node
def isSystem: Boolean = isPolicyServer
@@ -775,6 +1253,60 @@ final case class JSecurityToken(kind: String, token: String)
final case class JNodeProperty(name: String, value: ConfigValue, mode: Option[String], provider: Option[String])
+sealed trait NodeFactChangeEvent {
+ def name: String
+ def debugString: String
+}
+
+object NodeFactChangeEvent {
+ final case class NewPending(node: NodeFact, attrs: SelectFacts) extends NodeFactChangeEvent {
+ override val name: String = "newPending"
+ override def debugString: String = s"[${name}] node '${node.fqdn}' (${node.id.value})"
+ }
+ final case class UpdatedPending(oldNode: NodeFact, newNode: NodeFact, attrs: SelectFacts) extends NodeFactChangeEvent {
+ override val name: String = "updatedPending"
+ override def debugString: String = s"[${name}] node '${newNode.fqdn}' (${newNode.id.value})"
+ }
+ final case class Accepted(node: NodeFact, attrs: SelectFacts) extends NodeFactChangeEvent {
+ override val name: String = "accepted"
+ override def debugString: String = s"[${name}] node '${node.fqdn}' (${node.id.value})"
+ }
+ final case class Refused(node: NodeFact, attrs: SelectFacts) extends NodeFactChangeEvent {
+ override val name: String = "refused"
+ override def debugString: String = s"[${name}] node '${node.fqdn}' (${node.id.value})"
+ }
+ final case class Updated(oldNode: NodeFact, newNode: NodeFact, attrs: SelectFacts) extends NodeFactChangeEvent {
+ override val name: String = "updatedAccepted"
+ override def debugString: String = s"[${name}] node '${newNode.fqdn}' (${newNode.id.value})"
+ }
+ final case class Deleted(node: NodeFact, attrs: SelectFacts) extends NodeFactChangeEvent {
+ override val name: String = "deleted"
+ override def debugString: String = s"[${name}] node '${node.fqdn}' (${node.id.value})"
+ }
+ final case class Noop(nodeId: NodeId, attrs: SelectFacts) extends NodeFactChangeEvent {
+ override val name: String = "noop"
+ override def debugString: String = s"[${name}] node '${nodeId.value}' "
+ }
+}
+
+final case class ChangeContext(
+ modId: ModificationId,
+ actor: EventActor,
+ eventDate: DateTime,
+ message: Option[String],
+ actorIp: Option[String]
+)
+
+object ChangeContext {
+ def newForRudder(msg: Option[String] = None, actorIp: Option[String] = None) =
+ ChangeContext(ModificationId(java.util.UUID.randomUUID.toString), eventlog.RudderEventActor, DateTime.now(), msg, actorIp)
+}
+
+final case class NodeFactChangeEventCC(
+ event: NodeFactChangeEvent,
+ cc: ChangeContext
+)
+
object NodeFactSerialisation {
// we need to have several object to avoid:
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactChangeEventCallback.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactChangeEventCallback.scala
new file mode 100644
index 00000000000..762d5771f50
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactChangeEventCallback.scala
@@ -0,0 +1,385 @@
+/*
+ *************************************************************************************
+ * Copyright 2023 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.facts.nodes
+
+import com.normation.errors.IOResult
+import com.normation.eventlog.ModificationId
+import com.normation.inventory.domain.AcceptedInventory
+import com.normation.inventory.domain.InventoryStatus
+import com.normation.inventory.domain.NodeId
+import com.normation.inventory.domain.PendingInventory
+import com.normation.inventory.domain.RemovedInventory
+import com.normation.rudder.batch.AsyncDeploymentActor
+import com.normation.rudder.batch.AutomaticStartDeployment
+import com.normation.rudder.batch.UpdateDynamicGroups
+import com.normation.rudder.domain.eventlog.AcceptNodeEventLog
+import com.normation.rudder.domain.eventlog.DeleteNodeEventLog
+import com.normation.rudder.domain.eventlog.InventoryLogDetails
+import com.normation.rudder.domain.eventlog.RefuseNodeEventLog
+import com.normation.rudder.domain.logger.NodeLoggerPure
+import com.normation.rudder.domain.nodes.ModifyNodeDiff
+import com.normation.rudder.facts.nodes.MinimalNodeFactInterface.toNode
+import com.normation.rudder.repository.CachedRepository
+import com.normation.rudder.repository.EventLogRepository
+import com.normation.rudder.services.nodes.history.impl.FactLogData
+import com.normation.rudder.services.nodes.history.impl.InventoryHistoryJdbcRepository
+import com.normation.rudder.services.reports.CacheComplianceQueueAction
+import com.normation.rudder.services.reports.CacheExpectedReportAction
+import com.normation.rudder.services.reports.InvalidateCache
+import com.normation.utils.StringUuidGenerator
+import org.joda.time.DateTime
+import zio.ZIO
+
+/*
+ * This file store callbacks for node events.
+ * The canonical example case is event log records
+ */
+trait NodeFactChangeEventCallback {
+ def name: String
+ def run(change: NodeFactChangeEventCC): IOResult[Unit]
+}
+
+case class CoreNodeFactChangeEventCallback(
+ name: String,
+ exec: NodeFactChangeEventCC => IOResult[Unit]
+) extends NodeFactChangeEventCallback {
+ override def run(change: NodeFactChangeEventCC): IOResult[Unit] = {
+ exec(change)
+ }
+}
+
+/*
+ * A call back that give a basic log in webapp logs with the expected log level,
+ * so that other callbacks don't have to do it
+ */
+class AppLogNodeFactChangeEventCallback() extends NodeFactChangeEventCallback {
+ override def name: String = "node-fact-cec: webapp log"
+
+ override def run(change: NodeFactChangeEventCC): IOResult[Unit] = {
+ change.event match {
+ case NodeFactChangeEvent.UpdatedPending(old, next, attrs) =>
+ NodeLoggerPure.debug(s"Pending node '${next.fqdn}' [${next.id.value}]' was updated")
+ case NodeFactChangeEvent.Updated(old, next, attrs) =>
+ NodeLoggerPure.debug(s"Node '${next.fqdn}' [${next.id.value}]' was updated")
+ case NodeFactChangeEvent.NewPending(node, attrs) =>
+ NodeLoggerPure.info(s"New pending node: '${node.fqdn}' [${node.id.value}]'")
+ case NodeFactChangeEvent.Accepted(node, attrs) =>
+ NodeLoggerPure.info(s"New accepted node: '${node.fqdn}' [${node.id.value}]'")
+ case NodeFactChangeEvent.Refused(node, attrs) =>
+ NodeLoggerPure.info(s"Pending node '${node.fqdn}' [${node.id.value}]' was refused")
+ case NodeFactChangeEvent.Deleted(node, attrs) =>
+ NodeLoggerPure.info(s"Node '${node.fqdn}' [${node.id.value}]' was deleted")
+ case NodeFactChangeEvent.Noop(nodeId, attrs) =>
+ NodeLoggerPure.debug(s"No change for node '${nodeId.value}'")
+ }
+ }
+}
+
+/*
+ * A callback that trigger a dynamic group update on change.
+ * We still start a generation after, because even without a group change, properties and other
+ * things can lead to a generation update.
+ */
+class GenerationOnChange(
+ updateDynamicGroups: UpdateDynamicGroups,
+ asyncDeploymentAgent: AsyncDeploymentActor,
+ uuidGen: StringUuidGenerator
+) extends NodeFactChangeEventCallback {
+
+ override def name: String = "node-fact-ecc: update dyn group and start-generation-on-change"
+
+ private[nodes] def startGeneration(nodeId: NodeId): IOResult[Unit] = {
+ NodeLoggerPure.info(
+ s"Update in node '${nodeId.value}' inventories main information detected: triggering dynmaci group update and a policy generation"
+ ) *>
+ IOResult.attempt(updateDynamicGroups.startManualUpdate) *>
+ IOResult.attempt(
+ asyncDeploymentAgent ! AutomaticStartDeployment(
+ ModificationId(uuidGen.newUuid),
+ com.normation.rudder.domain.eventlog.RudderEventActor
+ )
+ )
+ }
+
+ override def run(change: NodeFactChangeEventCC): IOResult[Unit] = {
+ change.event match {
+ case NodeFactChangeEvent.NewPending(node, attrs) => ZIO.unit
+ case NodeFactChangeEvent.UpdatedPending(oldNode, newNode, attrs) => ZIO.unit
+ case NodeFactChangeEvent.Accepted(node, attrs) => startGeneration(node.id)
+ case NodeFactChangeEvent.Refused(node, attrs) => ZIO.unit
+ case NodeFactChangeEvent.Updated(oldNode, newNode, attrs) => startGeneration(newNode.id)
+ case NodeFactChangeEvent.Deleted(node, attrs) => startGeneration(node.id)
+ case NodeFactChangeEvent.Noop(nodeId, attrs) => ZIO.unit
+ }
+ }
+}
+
+/*
+ * Callback related to cache invalidation when a node changes
+ */
+class CacheInvalidateNodeFactEventCallback(
+ cacheExpectedReports: InvalidateCache[CacheExpectedReportAction],
+ cacheConfiguration: InvalidateCache[CacheComplianceQueueAction],
+ cacheToClear: List[CachedRepository]
+) extends NodeFactChangeEventCallback {
+
+ import com.normation.rudder.services.reports.CacheExpectedReportAction._
+
+ override def name: String = "node-fact-cec: invalidate caches"
+
+ override def run(change: NodeFactChangeEventCC): IOResult[Unit] = {
+ change.event match {
+ case NodeFactChangeEvent.NewPending(node, attrs) => ZIO.unit
+ case NodeFactChangeEvent.UpdatedPending(oldNode, newNode, attrs) => ZIO.unit
+ case NodeFactChangeEvent.Accepted(node, attrs) =>
+ // ping the NodeConfiguration Cache and NodeCompliance Cache about this new node
+ val i = InsertNodeInCache(node.id)
+ for {
+ _ <- cacheConfiguration
+ .invalidateWithAction(Seq((node.id, CacheComplianceQueueAction.ExpectedReportAction(i))))
+ .chainError(s"Error when adding node ${node.id.value} to node configuration cache")
+ _ <- cacheExpectedReports
+ .invalidateWithAction(Seq((node.id, i)))
+ .chainError(s"Error when adding node ${node.id.value} to compliance cache")
+ _ <- ZIO.foreach(cacheToClear)(c => IOResult.attempt(c.clearCache()))
+ } yield {
+ ()
+ }
+ case NodeFactChangeEvent.Refused(node, attrs) => ZIO.unit
+ case NodeFactChangeEvent.Updated(oldNode, newNode, attrs) => ZIO.unit
+ case NodeFactChangeEvent.Deleted(node, attrs) =>
+ val a = CacheExpectedReportAction.RemoveNodeInCache(node.id)
+ for {
+ _ <- NodeLoggerPure.Delete.debug(s" - remove node ${node.id.value} from compliance and expected report cache")
+ _ <-
+ cacheConfiguration
+ .invalidateWithAction(Seq((node.id, CacheComplianceQueueAction.ExpectedReportAction(a))))
+ .catchAll(err => {
+ NodeLoggerPure.Delete
+ .error(s"Error when removing node ${node.id.value} from node configuration cache: ${err.fullMsg}")
+ })
+ _ <- cacheExpectedReports
+ .invalidateWithAction(Seq((node.id, a)))
+ .catchAll(err =>
+ NodeLoggerPure.Delete.error(s"Error when removing node ${node.id.value} from compliance cache: ${err.fullMsg}")
+ )
+ } yield ()
+
+ case NodeFactChangeEvent.Noop(nodeId, attrs) => NodeLoggerPure.debug(s"No change for node '${nodeId.value}'")
+ }
+ }
+}
+
+/*
+ * Manage event logs related to nodes: register a change in properties, a node acceptation, etc
+ */
+class EventLogsNodeFactChangeEventCallback(
+ eventLogRepository: EventLogRepository
+) extends NodeFactChangeEventCallback {
+ override def name: String = "node-fact-cec: register even log"
+
+ override def run(change: NodeFactChangeEventCC): IOResult[Unit] = {
+ def modifyEventLog(
+ cc: ChangeContext,
+ old: MinimalNodeFactInterface,
+ next: MinimalNodeFactInterface
+ ): IOResult[Unit] = {
+ val diff = ModifyNodeDiff(toNode(old), toNode(next))
+ eventLogRepository.saveModifyNode(cc.modId, cc.actor, diff, cc.message, cc.eventDate).unit
+ }
+
+ change.event match {
+ case NodeFactChangeEvent.UpdatedPending(old, next, attrs) => modifyEventLog(change.cc, old, next)
+ case NodeFactChangeEvent.Updated(old, next, attrs) => modifyEventLog(change.cc, old, next)
+ case NodeFactChangeEvent.NewPending(node, attrs) => ZIO.unit
+ case NodeFactChangeEvent.Accepted(node, attrs) =>
+ val log = AcceptNodeEventLog.fromInventoryLogDetails(
+ principal = change.cc.actor,
+ creationDate = change.cc.eventDate,
+ inventoryDetails = InventoryLogDetails(
+ nodeId = node.id,
+ inventoryVersion = node.lastInventoryDate.getOrElse(node.factProcessedDate),
+ hostname = node.fqdn,
+ fullOsName = node.os.fullName,
+ actorIp = change.cc.actorIp.getOrElse("actor ip unknown")
+ )
+ )
+ eventLogRepository
+ .saveEventLog(change.cc.modId, log)
+ .tapBoth(
+ error =>
+ NodeLoggerPure.PendingNode
+ .warn(s"Node '${node.fqdn}' [${node.id.value}] accepted, but the action couldn't be logged"),
+ ok => NodeLoggerPure.PendingNode.debug(s"Successfully accepted node '${node.fqdn}' [${node.id.value}]")
+ )
+ .unit
+ case NodeFactChangeEvent.Refused(node, attrs) =>
+ val log = RefuseNodeEventLog.fromInventoryLogDetails(
+ principal = change.cc.actor,
+ creationDate = change.cc.eventDate,
+ inventoryDetails = InventoryLogDetails(
+ nodeId = node.id,
+ inventoryVersion = node.lastInventoryDate.getOrElse(node.factProcessedDate),
+ hostname = node.fqdn,
+ fullOsName = node.os.fullName,
+ actorIp = change.cc.actorIp.getOrElse("actor ip unknown")
+ )
+ )
+ eventLogRepository
+ .saveEventLog(change.cc.modId, log)
+ .tapBoth(
+ error =>
+ NodeLoggerPure.PendingNode
+ .warn(s"Node '${node.fqdn}' [${node.id.value}] refused, but the action couldn't be logged"),
+ ok => NodeLoggerPure.PendingNode.debug(s"Successfully refused node '${node.fqdn}' [${node.id.value}]")
+ )
+ .unit
+ case NodeFactChangeEvent.Deleted(node, attrs) =>
+ val log = DeleteNodeEventLog.fromInventoryLogDetails(
+ None,
+ principal = change.cc.actor,
+ creationDate = change.cc.eventDate,
+ inventoryDetails = InventoryLogDetails(
+ node.id,
+ node.lastInventoryDate.getOrElse(node.factProcessedDate),
+ node.fqdn,
+ node.os.fullName,
+ change.cc.actorIp.getOrElse("actor ip unknown")
+ )
+ )
+ eventLogRepository
+ .saveEventLog(change.cc.modId, log)
+ .tapBoth(
+ error =>
+ NodeLoggerPure.PendingNode
+ .warn(s"Node '${node.fqdn}' [${node.id.value}] deleted, but the action couldn't be stored in eventlogs"),
+ ok => NodeLoggerPure.debug(s"Successfully deleted node '${node.fqdn}' [${node.id.value}]")
+ )
+ .unit
+
+ case NodeFactChangeEvent.Noop(nodeId, attrs) => ZIO.unit
+ }
+ }
+}
+
+/*
+ * Keep a trace of the full inventory of the node where we need to.
+ * This happens on acceptation/refusal
+ */
+class HistorizeNodeState(
+ historyRepos: InventoryHistoryJdbcRepository,
+ sourceFactStorage: NodeFactStorage,
+ gitFactStorage: NodeFactStorage,
+ cleanUpImmediately: Boolean
+) extends NodeFactChangeEventCallback {
+
+ override def name: String = "node-fact-ecc: historize node fact on choice"
+
+ override def run(change: NodeFactChangeEventCC): IOResult[Unit] = {
+
+ def save(node: MinimalNodeFactInterface, eventDate: DateTime, alsoJDBC: Boolean, status: InventoryStatus): IOResult[Unit] = {
+ // we want to save the fact with everything
+ implicit val attrs = SelectFacts.all
+ if (gitFactStorage == NoopFactStorage && !alsoJDBC) ZIO.unit
+ else {
+ (if (status == PendingInventory) sourceFactStorage.getPending(node.id)
+ else sourceFactStorage.getAccepted(node.id)).flatMap { res =>
+ val nf = res match {
+ case Some(x) => x
+ case None => // in case of refuse event, node is already deleted
+ NodeFact.fromMinimal(node)
+ }
+ for {
+ _ <- ZIO.when(alsoJDBC)(historyRepos.save(node.id, FactLogData(nf, change.cc.actor, AcceptedInventory), eventDate))
+ _ <- gitFactStorage.save(nf)
+ } yield ()
+ }
+ }
+ }
+
+ def delete(nodeId: NodeId): IOResult[Unit] = {
+ /*
+ * This hook registers the deletion events into postgresql `nodefacts` table so that the inventory accept/refuse
+ * fact can be latter cleaned-up.
+ */
+ (
+ (
+ if (cleanUpImmediately) {
+ historyRepos.delete(nodeId)
+ } else { // save delete event, clean-up will be automatically done by script
+ historyRepos.saveDeleteEvent(nodeId, change.cc.eventDate, change.cc.actor)
+ }
+ ).catchAll(err => {
+ NodeLoggerPure
+ .warn(s"Error when updating node '${nodeId.value}' historical inventory information in base: ${err.fullMsg}")
+ })
+ ) *>
+ NodeLoggerPure.Delete.debug(s" - delete fact about node '${nodeId.value}'") *>
+ gitFactStorage
+ .changeStatus(nodeId, RemovedInventory)
+ .catchAll(err =>
+ NodeLoggerPure.info(s"Error when trying to update fact when deleting node '${nodeId.value}': ${err.fullMsg}")
+ )
+ .unit
+ }
+
+ change.event match {
+ case NodeFactChangeEvent.NewPending(node, attrs) =>
+ NodeLoggerPure.debug(s"Save new in node fact fs") *>
+ save(node, change.cc.eventDate, false, PendingInventory)
+ case NodeFactChangeEvent.UpdatedPending(oldNode, newNode, attrs) =>
+ NodeLoggerPure.debug(s"Update pending in node fact fs") *>
+ save(newNode, change.cc.eventDate, false, PendingInventory)
+ case NodeFactChangeEvent.Accepted(node, attrs) =>
+ NodeLoggerPure.debug(s"Accept in node fact fs and postgres") *>
+ save(node, change.cc.eventDate, true, AcceptedInventory) // callback done post accept
+ case NodeFactChangeEvent.Refused(node, attrs) =>
+ NodeLoggerPure.debug(s"Refused in node fact fs and postgres") *>
+ save(node, change.cc.eventDate, true, AcceptedInventory)
+ case NodeFactChangeEvent.Updated(oldNode, newNode, attrs) =>
+ NodeLoggerPure.debug(s"Update in node fact fs") *>
+ save(newNode, change.cc.eventDate, false, AcceptedInventory)
+ case NodeFactChangeEvent.Deleted(node, attrs) =>
+ NodeLoggerPure.debug(s"Delete in node fact fs") *>
+ delete(node.id)
+ case NodeFactChangeEvent.Noop(nodeId, attrs) =>
+ NodeLoggerPure.debug(s"noop") *>
+ ZIO.unit
+ }
+ }
+}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactRepository.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactRepository.scala
index 7f8bfcf554f..e9c9395335a 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactRepository.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactRepository.scala
@@ -37,51 +37,164 @@
package com.normation.rudder.facts.nodes
-import NodeFactSerialisation._
-import better.files.File
import com.normation.errors._
import com.normation.errors.IOResult
import com.normation.inventory.domain._
-import com.normation.rudder.domain.logger.NodeLogger
-import com.normation.rudder.git.GitItemRepository
-import com.normation.rudder.git.GitRepositoryProvider
-import java.nio.charset.StandardCharsets
-import org.eclipse.jgit.lib.PersonIdent
+import com.normation.inventory.services.core.ReadOnlySoftwareDAO
+import com.normation.rudder.domain.Constants
+import com.normation.rudder.domain.logger.NodeLoggerPure
+import com.normation.rudder.domain.nodes.NodeState
+import com.softwaremill.quicklens._
import zio._
-import zio.json._
+import zio.concurrent.ReentrantLock
import zio.stream.ZStream
import zio.syntax._
/*
- * Serialize a fact type (to/from JSON), for example nodes.
- * The format is versioned so that we are able to unserialize old files into newer domain representation.
+ * NodeFactRepository is the main interface between Rudder user space and nodes. It manages
+ * the whole persistence and consistency, efficient access to a (core) set of information on
+ * nodes, and access permissions.
*
- * We store a fileFormat and the serialized object type.
- * To let more space for evolution, file format will be a string even if it should be parsed as an int.
+ * The basic contract regarding performance is that:
+ * - saving things is slow and accounts for the consistency of data view,
+ * - view on the subset of node fact that matches minimal API / core node fact is fast (~in memory map)
+ * - access to other data is slow and need a cold storage retrieval
+ *
+ *
+ * The typical use case we need to be able to handle:
+ * - save a new inventory (full node fact, pending)
+ * - save an inventory update
+ * - save the audit mode change or node scheduling
+ * - save a new property
+ *
+ * Getting:
+ * - fast access to node code info for [computing compliance, access to node main inventory variable, display node info...]
+ * - get the whole inventory APART software and process (b/c too slow)
+ * - get only software for the node
+ *
+ * Note: is it not the same to retrieve "most of nodefact" and "node fact", because we can have extreme performance
+ * impacts for just some, rarely used (or use only on some nodes), information. Typically:
+ * - software ;
+ * - process ;
+ * - some hardware information on very complex harware.
+ * And in all case,
*
- * There's two parameter, one minimal (A) that allows to identify where the fact should be store (typically, it's a
- * kind of ID), and (B) which the whole fact to serialize. There should exists a constraint of derivability from B to A,
- * but it's not modeled.
*/
-trait SerializeFacts[A, B] {
+trait NodeFactRepository {
- def fileFormat: String
- def entity: String
+ /*
+ * Add a call back that will be called when a change occurs.
+ * The callbacks are not ordered and not blocking and will have a short time-out
+ * on them, the caller will need to manage that constraint.
+ */
+ def registerChangeCallbackAction(callback: NodeFactChangeEventCallback): IOResult[Unit]
- def toJson(data: B): IOResult[String]
+ /*
+ * Get the status of the node, or RemovedStatus if it is
+ * not found.
+ */
+ def getStatus(id: NodeId): IOResult[InventoryStatus]
- // this is just a relative path from a virtual root, for example for node it will be: "accepted/node-uuid.json"
- def getEntityPath(id: A): String
+ /*
+ * Translation between old inventory status and new SelectNodeStatus for IOResult methods
+ */
+ def statusCompat[A](status: InventoryStatus, f: SelectNodeStatus => IOResult[A]): IOResult[A] = {
+ status match {
+ case AcceptedInventory => f(SelectNodeStatus.Accepted)
+ case PendingInventory => f(SelectNodeStatus.Pending)
+ case RemovedInventory => Inconsistency("You can not query deleted nodes").fail
+ }
+ }
-}
+ /*
+ * Translation between old inventory status and new SelectNodeStatus for IOStream methods
+ */
+ def statusStreamCompat[A](status: InventoryStatus, f: SelectNodeStatus => IOStream[A]): IOStream[A] = {
+ status match {
+ case AcceptedInventory => f(SelectNodeStatus.Accepted)
+ case PendingInventory => f(SelectNodeStatus.Pending)
+ case RemovedInventory => ZStream.fromZIO(Inconsistency("You can not query deleted nodes").fail)
+ }
+ }
+
+ /*
+ * Get node on given status
+ */
+ def get(nodeId: NodeId)(implicit status: SelectNodeStatus = SelectNodeStatus.Any): IOResult[Option[CoreNodeFact]]
+
+ def getCompat(nodeId: NodeId, status: InventoryStatus): IOResult[Option[CoreNodeFact]] = {
+ statusCompat(status, get(nodeId)(_))
+ }
+
+ /*
+ * Return the node fact corresponding to the given node id with
+ * the fields from select mode "ignored" set to empty.
+ */
+ def slowGet(nodeId: NodeId)(implicit
+ status: SelectNodeStatus = SelectNodeStatus.Any,
+ attrs: SelectFacts = SelectFacts.default
+ ): IOResult[Option[NodeFact]]
+
+ def slowGetCompat(nodeId: NodeId, status: InventoryStatus, attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ statusCompat(status, slowGet(nodeId)(_, attrs))
+ }
-trait NodeFactStorage {
+ def getNodesbySofwareName(softName: String): IOResult[List[(NodeId, Software)]]
/*
- * Save node fact in the status given in the corresponding attribute.
- * No check will be done.
+ * get all node facts.
+ * SelectStatus allows to choose which nodes are retrieved (pending, accepted, all)
*/
- def save(nodeFact: NodeFact): IOResult[Unit]
+ def getAll()(implicit status: SelectNodeStatus = SelectNodeStatus.Accepted): IOStream[CoreNodeFact]
+
+ def getAllCompat(status: InventoryStatus, attrs: SelectFacts): IOStream[CoreNodeFact] = {
+ statusStreamCompat(status, getAll()(_))
+ }
+
+ /*
+ * A version of getAll that allows to retrieve attributes out of CoreNodeFact at the
+ * price of a round trip to the cold storage.
+ * Implementation must be smart and ensure that if attrs == SelectFacts.none,
+ * then it reverts back to the quick version.
+ */
+ def slowGetAll()(implicit
+ status: SelectNodeStatus = SelectNodeStatus.Accepted,
+ attrs: SelectFacts = SelectFacts.default
+ ): IOStream[NodeFact]
+
+ def slowGetAllCompat(status: InventoryStatus, attrs: SelectFacts): IOStream[NodeFact] = {
+ statusStreamCompat(status, slowGetAll()(_, attrs))
+ }
+
+ ///// changes /////
+
+ /*
+ * Save (create or override) a core node fact
+ * Use "updateInventory` if you want to save in pending, it's likely what you want.
+ *
+ * Not that the diff is only done on the core properties
+ */
+ def save(
+ nodeFact: NodeFact
+ )(implicit cc: ChangeContext, attrs: SelectFacts = SelectFacts.all): IOResult[NodeFactChangeEventCC]
+
+ /*
+ * Save the full node fact.
+ * If some fields are marked as ignored, they must not be updated by the persistence layer
+ * (it's up to it to do it).
+ *
+ * Not sure it's interesting since we have "update inventory" ?
+ */
+ // def saveFull[A](nodeId: NodeId, fact: NodeFact)(implicit cc: ChangeContext, s: SelectFacts = SelectFacts.all): IOResult[Unit]
+
+ /*
+ * A method that will create in new node fact in pending, or
+ * update inventory part of the node with that nodeId in
+ * pending or in accepted.
+ */
+ def updateInventory(inventory: FullInventory, software: Option[Iterable[Software]])(implicit
+ cc: ChangeContext
+ ): IOResult[NodeFactChangeEventCC]
/*
* Change the status of the node with given id to given status.
@@ -89,201 +202,460 @@ trait NodeFactStorage {
* - if the target status is the current one, this function does nothing
* - if target status is "removed", persisted inventory is deleted
*/
- def changeStatus(nodeId: NodeId, status: InventoryStatus): IOResult[Unit]
+ def changeStatus(nodeId: NodeId, into: InventoryStatus)(implicit
+ cc: ChangeContext
+ ): IOResult[NodeFactChangeEventCC]
/*
- * Delete the node. Storage need to loop for any status and delete
- * any reference to that node.
+ * Delete any reference to that node id.
*/
- def delete(nodeId: NodeId): IOResult[Unit]
-
- def getAllPending(): IOStream[NodeFact]
- def getAllAccepted(): IOStream[NodeFact]
+ def delete(nodeId: NodeId)(implicit cc: ChangeContext): IOResult[NodeFactChangeEventCC]
}
/*
- * Implementaton that store nothing and that can be used in tests or when a pure
- * in-memory version of the nodeFactRepos is needed.
+ * A partial in memory implementation of the NodeFactRepository that persist (for cold storage)
+ * it's information in given backend.
+ *
+ * NodeFacts are split in two parts:
+ * - CoreNodeFacts are kept in memory which allows for fast lookup and search on main attributes
+ * - full NodeFacts are retrieved from cold storage on demand.
+ *
+ * The following operation are always persisted in cold storage and will be blocking:
+ * - create a new node fact
+ * - update an existing one
+ * - change status of a node
+ * - delete a node.
+ *
+ * Core node facts info are always saved.
+ * To be more precise on what is retrieved or saved for non-core nodeFact, you can use the `SelectFacts`
+ * parametrization which will restraint get/save only the specified info.
+ *
+ * Once initialized, that repository IS the truth for CoreNodeFact info. No change done by
+ * an other mean in the cold storage will be visible from Rudder without an explicit
+ * `fetchAndSync` call.
+ * Moreover, that repository is in charge to ensure consistency of states for nodes.
+ * Consequently, any change in a nodes must go through that repository, from inventory updates to
+ * node acceptation or properties setting.
+ *
+ * For change, that repos try to ensure that the backend does commit the change before having it done
+ * in memory. That arch does not scale to many backend, since once there is more than one, compensation
+ * strategy must be put into action to compensate for errors (see zio-workflow for that kind of things).
+ *
*/
-object NoopFactStorage extends NodeFactStorage {
- override def save(nodeFact: NodeFact): IOResult[Unit] = ZIO.unit
- override def changeStatus(nodeId: NodeId, status: InventoryStatus): IOResult[Unit] = ZIO.unit
- override def delete(nodeId: NodeId): IOResult[Unit] = ZIO.unit
- override def getAllPending(): IOStream[NodeFact] = ZStream.empty
- override def getAllAccepted(): IOStream[NodeFact] = ZStream.empty
-}
+object CoreNodeFactRepository {
+ def make(
+ storage: NodeFactStorage,
+ softByName: GetNodesbySofwareName,
+ callbacks: Chunk[NodeFactChangeEventCallback]
+ ): IOResult[CoreNodeFactRepository] = for {
+ _ <- InventoryDataLogger.debug("Getting pending node info for node fact repos")
+ pending <- storage.getAllPending()(SelectFacts.none).map(f => (f.id, f.toCore)).runCollect.map(_.toMap)
+ _ <- InventoryDataLogger.debug("Getting accepted node info for node fact repos")
+ accepted <- storage.getAllAccepted()(SelectFacts.none).map(f => (f.id, f.toCore)).runCollect.map(_.toMap)
+ _ <- InventoryDataLogger.debug("Creating node fact repos")
+ repo <- make(storage, softByName, pending, accepted, callbacks)
+ } yield {
+ repo
+ }
-/*
- * We have only one git for all fact repositories. This is the one managing semaphore, init, etc.
- * All fact repositories will be a subfolder on it:
- * - /var/rudder/fact-repository/nodes
- * - /var/rudder/fact-repository/rudder-config
- * - /var/rudder/fact-repository/groups
- * etc
- */
+ def make(
+ storage: NodeFactStorage,
+ softByName: GetNodesbySofwareName,
+ pending: Map[NodeId, CoreNodeFact],
+ accepted: Map[NodeId, CoreNodeFact],
+ callbacks: Chunk[NodeFactChangeEventCallback]
+ ): UIO[CoreNodeFactRepository] = for {
+ p <- Ref.make(pending)
+ a <- Ref.make(accepted)
+ lock <- ReentrantLock.make()
+ cbs <- Ref.make(callbacks)
+ } yield {
+ new CoreNodeFactRepository(storage, softByName, p, a, cbs, lock)
+ }
-object GitNodeFactRepositoryImpl {
+}
- final case class NodeFactArchive(
- entity: String,
- fileFormat: String,
- node: NodeFact
- )
+// we have some specialized services / materialized view for complex queries. Implementation can manage cache and
+// react to callbacks (update events) to manage consistency
+trait GetNodesbySofwareName {
+ def apply(softName: String): IOResult[List[(NodeId, Software)]]
+}
- implicit val codecNodeFactArchive: JsonCodec[NodeFactArchive] = DeriveJsonCodec.gen
+// default implementation is just a proxy on top of software dao
+class SoftDaoGetNodesbySofwareName(val softwareDao: ReadOnlySoftwareDAO) extends GetNodesbySofwareName {
+ override def apply(softName: String): IOResult[List[(NodeId, Software)]] = {
+ softwareDao.getNodesbySofwareName(softName)
+ }
}
/*
- * Nodes are stored in the git facts repo under the relative path "nodes".
- * They are then stored:
- * - under nodes/pending or nodes/accepted given their status (which means that changing status of a node is
- * a special operation)
+ * The core node fact repository save:
+ * - CoreNodeFact in a local map that is always in sync with persisted layers
+ * - extension data (for inventory) in external caches
+ *
+ * It also provide et default implementation for getting/saving CoreNodeFact and Full facts
+ * thanks to the provided NodeFactStorage. Other getter/saver will need to be implemented
+ * by your own.
+ *
+ * Rudder server (id=root) is special among nodes. It can be disabled, non system, deleted, etc.
*/
-class GitNodeFactRepositoryImpl(
- override val gitRepo: GitRepositoryProvider,
- groupOwner: String,
- actuallyCommit: Boolean
-) extends NodeFactStorage with GitItemRepository with SerializeFacts[(NodeId, InventoryStatus), NodeFact] {
-
- override val relativePath = "nodes"
- override val entity: String = "node"
- override val fileFormat: String = "10"
- val committer = new PersonIdent("rudder-fact", "email not set")
-
- if (actuallyCommit) {
- NodeLogger.info(s"Nodes changes will be historized in Git in ${gitRepo.rootDirectory.pathAsString}/nodes")
- } else {
- NodeLogger.info(
- s"Nodes changes won't be historized in Git, only last state is stored in ${gitRepo.rootDirectory.pathAsString}/nodes"
- )
+class CoreNodeFactRepository(
+ storage: NodeFactStorage,
+ softwareByName: GetNodesbySofwareName,
+ pendingNodes: Ref[Map[NodeId, CoreNodeFact]],
+ acceptedNodes: Ref[Map[NodeId, CoreNodeFact]],
+ callbacks: Ref[Chunk[NodeFactChangeEventCallback]],
+ lock: ReentrantLock,
+ cbTimeout: zio.Duration = 5.seconds
+) extends NodeFactRepository {
+ import NodeFactChangeEvent._
+
+ // debug log
+// (for {
+// p <- pendingNodes.get.map(_.values.map(_.id.value).mkString(", "))
+// a <- acceptedNodes.get.map(_.values.map(_.id.value).mkString(", "))
+// _ <- InventoryDataLogger.debug(s"Loaded node fact repos with: \n - pending: ${p} \n - accepted: ${a}")
+// } yield ()).runNow
+
+ override def registerChangeCallbackAction(
+ callback: NodeFactChangeEventCallback
+ ): IOResult[Unit] = {
+ callbacks.update(_.appended(callback))
}
- override def getEntityPath(id: (NodeId, InventoryStatus)): String = {
- s"${id._2.name}/${id._1.value}.json"
+ /*
+ * This method will need some thoughts:
+ * - do we want to fork and timeout each callbacks ? likely so
+ * - do we want to parallel exec them ? likely so, the user can build his own callback sequencer callback if he wants
+ */
+ private[nodes] def runCallbacks(e: NodeFactChangeEventCC): IOResult[Unit] = {
+ for {
+ cs <- callbacks.get
+ _ <- ZIO.foreachParDiscard(cs)(_.run(e)).timeout(cbTimeout).forkDaemon
+ } yield ()
+ }
+
+ override def getStatus(id: NodeId): IOResult[InventoryStatus] = {
+ pendingNodes.get.flatMap { p =>
+ if (p.keySet.contains(id)) PendingInventory.succeed
+ else {
+ acceptedNodes.get.flatMap(a => {
+ if (a.keySet.contains(id)) AcceptedInventory.succeed
+ else RemovedInventory.succeed
+ })
+ }
+ }
}
- def getFile(id: NodeId, status: InventoryStatus): File = {
- gitRepo.rootDirectory / relativePath / getEntityPath((id, status))
+ private[nodes] def getOnRef(ref: Ref[Map[NodeId, CoreNodeFact]], nodeId: NodeId): IOResult[Option[CoreNodeFact]] = {
+ ref.get.map(_.get(nodeId))
}
/*
- * serialize the inventory into a normalized JSON string.
- * As we want it to be human readable and searchable, we will use an indented format.
+ * Require to re-sync from cold storage cache info.
+ * It will lead to a diff and subsequent callbacks for any changes
*/
- def toJson(nodeFact: NodeFact): IOResult[String] = {
- import GitNodeFactRepositoryImpl._
- NodeFactArchive(entity, fileFormat, nodeFact).toJsonPretty.succeed
+ def fetchAndSync(nodeId: NodeId)(implicit cc: ChangeContext): IOResult[NodeFactChangeEventCC] = {
+ implicit val attrs: SelectFacts = SelectFacts.default
+ for {
+ a <- storage.getAccepted(nodeId)
+ p <- storage.getPending(nodeId)
+ c <- get(nodeId)(SelectNodeStatus.Any)
+ diff <- (a, p, c) match {
+ case (None, None, _) => delete(nodeId)
+ case (None, Some(x), _) =>
+ saveOn(pendingNodes, x.toCore).map { e =>
+ e.updateWith(StorageChangeEventSave.Created(x, attrs))
+ .toChangeEvent(nodeId, PendingInventory, cc)
+ }
+ case (Some(x), _, _) =>
+ saveOn(acceptedNodes, x.toCore).map { e =>
+ e.updateWith(StorageChangeEventSave.Created(x, attrs))
+ .toChangeEvent(nodeId, AcceptedInventory, cc)
+ }
+ }
+ } yield diff
}
- private[nodes] def getAll(base: File): IOStream[NodeFact] = {
- // TODO should be from git head, not from file directory
- val stream = ZStream.fromIterator(base.collectChildren(_.extension(includeDot = true, includeAll = true) == Some(".json")))
- stream
- .mapError(ex => SystemError("Error when reading node fact persisted file", ex))
- .mapZIO(f =>
- f.contentAsString(StandardCharsets.UTF_8).fromJson[NodeFact].toIO.chainError(s"Error when decoding ${f.pathAsString}")
- )
+ override def get(nodeId: NodeId)(implicit status: SelectNodeStatus = SelectNodeStatus.Any): IOResult[Option[CoreNodeFact]] = {
+ status match {
+ case SelectNodeStatus.Pending =>
+ getOnRef(pendingNodes, nodeId)
+ case SelectNodeStatus.Accepted =>
+ getOnRef(acceptedNodes, nodeId)
+ case SelectNodeStatus.Any =>
+ getOnRef(acceptedNodes, nodeId).flatMap(opt => opt.fold(getOnRef(pendingNodes, nodeId))(Some(_).succeed))
+ }
}
- override def getAllPending(): IOStream[NodeFact] = getAll(gitRepo.rootDirectory / relativePath / PendingInventory.name)
- override def getAllAccepted(): IOStream[NodeFact] = getAll(gitRepo.rootDirectory / relativePath / AcceptedInventory.name)
+ override def slowGet(nodeId: NodeId)(implicit status: SelectNodeStatus, attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ (for {
+ optCNF <- get(nodeId)(status)
+ res <- optCNF match {
+ case None => None.succeed
+ case Some(v) =>
+ val fact = NodeFact.fromMinimal(v)
+ if (attrs == SelectFacts.none) {
+ Some(fact).succeed
+ } else {
+ (status match {
+ case SelectNodeStatus.Pending => storage.getPending(nodeId)(attrs)
+ case SelectNodeStatus.Accepted => storage.getAccepted(nodeId)(attrs)
+ case SelectNodeStatus.Any =>
+ storage.getAccepted(nodeId)(attrs).flatMap {
+ case Some(x) => Some(x).succeed
+ case None => storage.getPending(nodeId)(attrs)
+ }
+ }).flatMap {
+ case None =>
+ // here, we have the value in cache but not in cold storage.
+ // This is an inconsistency and likely going to pause problem latter on
+ // perhaps we should compensate, CoreNodeFactRepo should be the reference.
+ // At least log.
+ NodeLoggerPure.warn(
+ s"Inconsistency: node '${fact.fqdn}' [${fact.id.value}] was found in Rudder memory base but not in cold storage. " +
+ s"This is not supposed to be, perhaps cold storage was modified not through Rudder. This is likely to lead to consistency problem. " +
+ s"You should use Rudder API."
+ ) *> // in that case still return core fact
+ Some(fact).succeed
+ case Some(b) =>
+ Some(SelectFacts.mergeCore(v, b)(attrs)).succeed
+ }
+ }
+ }
+ } yield res)
+ }
- override def save(nodeFact: NodeFact): IOResult[Unit] = {
- if (nodeFact.rudderSettings.status == RemovedInventory) {
- InventoryDataLogger.info(
- s"Not persisting deleted node '${nodeFact.fqdn}' [${nodeFact.id.value}]: it has removed inventory status"
- ) *>
- ZIO.unit
+ private[nodes] def getAllOnRef[A](ref: Ref[Map[NodeId, CoreNodeFact]]): IOStream[CoreNodeFact] = {
+ ZStream.fromZIO(ref.get.map(_.values)).flatMap(x => ZStream.fromIterable(x))
+ }
+
+ override def getAll()(implicit status: SelectNodeStatus = SelectNodeStatus.Accepted): IOStream[CoreNodeFact] = {
+ status match {
+ case SelectNodeStatus.Pending => getAllOnRef(pendingNodes)
+ case SelectNodeStatus.Accepted => getAllOnRef(acceptedNodes)
+ case SelectNodeStatus.Any => getAllOnRef(pendingNodes) ++ getAllOnRef(acceptedNodes)
+ }
+ }
+
+ override def slowGetAll()(implicit status: SelectNodeStatus, attrs: SelectFacts): IOStream[NodeFact] = {
+ if (attrs == SelectFacts.none) {
+ getAll()(status).map(cnf => NodeFact.fromMinimal(cnf))
} else {
- for {
- json <- toJson(nodeFact)
- file = getFile(nodeFact.id, nodeFact.rudderSettings.status)
- _ <- IOResult.attempt(file.write(json))
- _ <- IOResult.attempt(file.setGroup(groupOwner))
- _ <- ZIO.when(actuallyCommit) {
- commitAddFile(
- committer,
- toGitPath(file.toJava),
- s"Save inventory facts for ${nodeFact.rudderSettings.status.name} node '${nodeFact.fqdn}' (${nodeFact.id.value})"
- )
- }
- } yield ()
+ status match {
+ case SelectNodeStatus.Pending => storage.getAllPending()(attrs)
+ case SelectNodeStatus.Accepted => storage.getAllAccepted()(attrs)
+ case SelectNodeStatus.Any => storage.getAllPending()(attrs) ++ storage.getAllAccepted()(attrs)
+ }
}
}
- // when we delete, we check for all path to also remove possible left-over
- // we may need to recreate pending/accepted directory, because git delete
- // empty directories.
- override def delete(nodeId: NodeId) = {
- ZIO.foreach(List(PendingInventory, AcceptedInventory)) { s =>
- val file = getFile(nodeId, s)
- ZIO.whenZIO(IOResult.attempt(file.exists)) {
- if (actuallyCommit) {
- commitRmFile(committer, toGitPath(file.toJava), s"Updating facts for node '${nodeId.value}': deleted")
- } else {
- IOResult.attempt(file.delete())
+ override def getNodesbySofwareName(softName: String): IOResult[List[(NodeId, Software)]] = {
+ softwareByName(softName)
+ }
+
+ /*
+ *
+ */
+ private def saveOn(ref: Ref[Map[NodeId, CoreNodeFact]], nodeFact: CoreNodeFact): IOResult[StorageChangeEventSave] = {
+ ref
+ .getAndUpdate(_ + ((nodeFact.id, nodeFact)))
+ .map { old =>
+ old.get(nodeFact.id) match {
+ case Some(n) =>
+ if (CoreNodeFact.same(n, nodeFact)) StorageChangeEventSave.Noop(nodeFact.id, SelectFacts.none)
+ else StorageChangeEventSave.Updated(NodeFact.fromMinimal(n), NodeFact.fromMinimal(nodeFact), SelectFacts.none)
+ case None => StorageChangeEventSave.Created(NodeFact.fromMinimal(nodeFact), SelectFacts.none)
}
}
- } *> checkInit()
}
- override def changeStatus(nodeId: NodeId, toStatus: InventoryStatus): IOResult[Unit] = {
- // pending and accepted are symmetric, utility function for the two cases
- def move(to: InventoryStatus) = {
- val from = if (to == AcceptedInventory) PendingInventory else AcceptedInventory
-
- val fromFile = getFile(nodeId, from)
- val toFile = getFile(nodeId, to)
- // check if fact already where it should
- ZIO.ifZIO(IOResult.attempt(fromFile.exists))(
- // however toFile exists, move, because if present it may be because a deletion didn't work and
- // we need to overwrite
- IOResult.attempt(fromFile.moveTo(toFile)(File.CopyOptions(overwrite = true))) *>
- ZIO.when(actuallyCommit) {
- commitMvDirectory(
- committer,
- toGitPath(fromFile.toJava),
- toGitPath(toFile.toJava),
- s"Updating facts for node '${nodeId.value}' to status: ${to.name}"
+ private def deleteOn(ref: Ref[Map[NodeId, CoreNodeFact]], nodeId: NodeId): IOResult[StorageChangeEventDelete] = {
+ ref
+ .getAndUpdate(_.removed(nodeId))
+ .map(old => {
+ old.get(nodeId) match {
+ case None => StorageChangeEventDelete.Noop(nodeId)
+ case Some(n) => StorageChangeEventDelete.Deleted(NodeFact.fromMinimal(n), SelectFacts.none)
+ }
+ })
+ }
+
+ private def checkRootProperties(node: NodeFact): IOResult[Unit] = {
+ // use cats validation
+ import cats.data._
+ import cats.implicits._
+
+ type ValidationResult = ValidatedNel[String, Unit]
+ val ok = ().validNel
+
+ def validateRoot(node: NodeFact): IOResult[Unit] = {
+ // transform a validation result to a Full | Failure
+ implicit class toIOResult(validation: ValidatedNel[String, List[Unit]]) {
+ def toZIO: IOResult[Unit] = {
+ validation.fold(
+ nel => Inconsistency(nel.toList.mkString("; ")).fail,
+ _ => ZIO.unit
)
- }, // if source file does not exist, check if dest is present. If present, assume it's ok, else error
+ }
+ }
- ZIO.whenZIO(IOResult.attempt(!toFile.exists)) {
- Inconsistency(
- s"Error when trying to move fact for node '${nodeId.value}' from '${fromFile.pathAsString}' to '${toFile.pathAsString}': missing files"
- ).fail
+ val checks: List[NodeFact => ValidationResult] = List(
+ (node: NodeFact) => { // root is enablef
+ if (node.rudderSettings.state == NodeState.Enabled) ok
+ else s"Root node must always be in '${NodeState.Enabled.name}' lifecycle state.".invalidNel
+ },
+ (node: NodeFact) => { // root is PolicyServer
+ if (node.isPolicyServer) ok
+ else "You can't change the 'policy server' nature of Root policy server".invalidNel
+ },
+ (node: NodeFact) => { // rootIsSystem
+ if (node.isSystem) ok
+ else "You can't change the 'system' nature of Root policy server".invalidNel
+ },
+ (node: NodeFact) => { // rootIsAccepted
+ if (node.rudderSettings.status == AcceptedInventory) ok
+ else "You can't change the 'status' of Root policy server, it must be accepted".invalidNel
}
)
+
+ checks.traverse(_(node)).toZIO
}
- (toStatus match {
- case RemovedInventory => delete(nodeId)
- case x => move(x)
- }).unit
+ ZIO.when(node.id == Constants.ROOT_POLICY_SERVER_ID)(validateRoot(node)).unit
}
- /*
- * check that everything is ok for that repo entities (typically: subfolder created, perm ok, etc)
- */
- def checkInit(): IOResult[Unit] = {
- val dirs = List(AcceptedInventory.name, PendingInventory.name)
- dirs.accumulate { dir =>
- val d = gitRepo.rootDirectory / relativePath / dir
+ def save(
+ nodeFact: NodeFact
+ )(implicit cc: ChangeContext, attrs: SelectFacts = SelectFacts.all): IOResult[NodeFactChangeEventCC] = {
+ checkRootProperties(nodeFact) *>
+ ZIO.scoped(
for {
- _ <- ZIO
- .whenZIO(IOResult.attempt(d.notExists)) {
- IOResult.attempt {
- d.createDirectories()
- d.setGroup(groupOwner)
- }
- }
- .chainError(s"Error when creating directory '${d.pathAsString}' for historising inventories: ${}")
- _ <- ZIO.whenZIO(IOResult.attempt(!d.isOwnerWritable)) {
- Inconsistency(
- s"Error, directory '${d.pathAsString}' must be a writable directory to allow inventory historisation"
- ).fail
+ _ <- lock.withLock
+ // here we persist all the core data with the provided solution
+ s <- storage.save(nodeFact)
+ // but then the diff are only done on the core elements
+ e <- nodeFact.rudderSettings.status match {
+ case RemovedInventory => // this case is ignored, we don't delete node based on status value
+ NodeFactChangeEventCC(Noop(nodeFact.id, attrs), cc).succeed
+ case PendingInventory =>
+ saveOn(pendingNodes, nodeFact.toCore).map(e => e.updateWith(s).toChangeEvent(nodeFact.id, PendingInventory, cc))
+ case AcceptedInventory =>
+ saveOn(acceptedNodes, nodeFact.toCore).map(e =>
+ e.updateWith(s).toChangeEvent(nodeFact.id, AcceptedInventory, cc)
+ )
}
- } yield ()
- }.unit
+ _ <- runCallbacks(e)
+ } yield e
+ )
+ }
+
+ override def changeStatus(nodeId: NodeId, into: InventoryStatus)(implicit
+ cc: ChangeContext
+ ): IOResult[NodeFactChangeEventCC] = {
+ if (nodeId == Constants.ROOT_POLICY_SERVER_ID && into != AcceptedInventory) {
+ Inconsistency(s"Rudder server (id='root' must be accepted").fail
+ } else {
+ ZIO.scoped(
+ for {
+ _ <- lock.withLock
+ _ <- storage.changeStatus(nodeId, into)
+ e <-
+ for {
+ pending <- getOnRef(pendingNodes, nodeId)
+ accepted <- getOnRef(acceptedNodes, nodeId)
+ e <- (into, pending, accepted) match {
+ case (RemovedInventory, Some(x), None) =>
+ deleteOn(pendingNodes, nodeId) *> NodeFactChangeEventCC(
+ Refused(NodeFact.fromMinimal(x), SelectFacts.none),
+ cc
+ ).succeed
+ case (RemovedInventory, None, Some(x)) =>
+ deleteOn(acceptedNodes, nodeId) *> NodeFactChangeEventCC(
+ Deleted(NodeFact.fromMinimal(x), SelectFacts.none),
+ cc
+ ).succeed
+ case (RemovedInventory, Some(_), Some(x)) =>
+ deleteOn(pendingNodes, nodeId) *>
+ deleteOn(acceptedNodes, nodeId) *>
+ NodeFactChangeEventCC(Deleted(NodeFact.fromMinimal(x), SelectFacts.none), cc).succeed
+ case (RemovedInventory, None, None) =>
+ NodeFactChangeEventCC(Noop(nodeId, SelectFacts.none), cc).succeed
+ case (_, None, None) =>
+ Inconsistency(
+ s"Error: node '${nodeId.value}' was not found in rudder (neither pending nor accepted nodes"
+ ).fail
+ case (AcceptedInventory, None, Some(_)) =>
+ NodeFactChangeEventCC(Noop(nodeId, SelectFacts.none), cc).succeed
+ case (AcceptedInventory, Some(x), None) =>
+ deleteOn(pendingNodes, nodeId) *> saveOn(
+ acceptedNodes,
+ x.modify(_.rudderSettings.status).setTo(AcceptedInventory)
+ ) *> NodeFactChangeEventCC(Accepted(NodeFact.fromMinimal(x), SelectFacts.none), cc).succeed
+ case (AcceptedInventory, Some(_), Some(_)) =>
+ deleteOn(pendingNodes, nodeId) *> NodeFactChangeEventCC(Noop(nodeId, SelectFacts.none), cc).succeed
+ case (PendingInventory, None, Some(x)) =>
+ deleteOn(acceptedNodes, nodeId) *> saveOn(
+ pendingNodes,
+ x.modify(_.rudderSettings.status).setTo(PendingInventory)
+ ) *> NodeFactChangeEventCC(
+ Deleted(NodeFact.fromMinimal(x), SelectFacts.none),
+ cc
+ ).succeed // not sure about the semantic here
+ case (PendingInventory, Some(_), None) =>
+ NodeFactChangeEventCC(Noop(nodeId, SelectFacts.none), cc).succeed
+ case (PendingInventory, Some(_), Some(x)) =>
+ deleteOn(acceptedNodes, nodeId) *> NodeFactChangeEventCC(
+ Deleted(NodeFact.fromMinimal(x), SelectFacts.none),
+ cc
+ ).succeed
+ }
+ } yield e
+ _ <- runCallbacks(e)
+ } yield e
+ )
+ }
+ }
+
+ override def delete(nodeId: NodeId)(implicit cc: ChangeContext): IOResult[NodeFactChangeEventCC] = {
+ ZIO.scoped(
+ for {
+ _ <- lock.withLock
+ cnf <- get(nodeId)(SelectNodeStatus.Any)
+ s <- storage.delete(nodeId)(SelectFacts.all)
+ e <- cnf match {
+ case Some(n) =>
+ if (n.rudderSettings.status == PendingInventory) {
+ deleteOn(pendingNodes, nodeId).map(_.updateWith(s).toChangeEvent(n, PendingInventory, cc))
+ } else {
+ deleteOn(acceptedNodes, nodeId).map(_.updateWith(s).toChangeEvent(n, AcceptedInventory, cc))
+ }
+ case None => NodeFactChangeEventCC(NodeFactChangeEvent.Noop(nodeId, SelectFacts.all), cc).succeed
+ }
+ _ <- runCallbacks(e)
+ } yield e
+ )
+ }
+
+ override def updateInventory(inventory: FullInventory, software: Option[Iterable[Software]])(implicit
+ cc: ChangeContext
+ ): IOResult[NodeFactChangeEventCC] = {
+ val nodeId = inventory.node.main.id
+ implicit val attrs = if (software.isEmpty) SelectFacts.noSoftware else SelectFacts.all
+ ZIO.scoped(
+ for {
+ _ <- lock.withLock
+ optPending <- getOnRef(pendingNodes, nodeId)
+ optFact <- optPending match {
+ case Some(f) => Some(f).succeed
+ case None => getOnRef(acceptedNodes, nodeId)
+ }
+ fact = optFact match {
+ case Some(f) => NodeFact.updateFullInventory(NodeFact.fromMinimal(f), inventory, software)
+ case None => NodeFact.newFromFullInventory(inventory, software)
+ }
+ e <- save(fact) // save already runs callbacks
+ } yield e
+ )
}
}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactServiceProxies.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactServiceProxies.scala
new file mode 100644
index 00000000000..e63b231f3af
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactServiceProxies.scala
@@ -0,0 +1,270 @@
+/*
+ *************************************************************************************
+ * Copyright 2023 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.facts.nodes
+
+import com.normation.errors.IOResult
+import com.normation.eventlog.EventActor
+import com.normation.eventlog.ModificationId
+import com.normation.inventory.domain.AcceptedInventory
+import com.normation.inventory.domain.Certificate
+import com.normation.inventory.domain.FullInventory
+import com.normation.inventory.domain.Inventory
+import com.normation.inventory.domain.InventoryError.Inconsistency
+import com.normation.inventory.domain.InventoryStatus
+import com.normation.inventory.domain.KeyStatus
+import com.normation.inventory.domain.MachineUuid
+import com.normation.inventory.domain.NodeId
+import com.normation.inventory.domain.PendingInventory
+import com.normation.inventory.domain.RemovedInventory
+import com.normation.inventory.domain.SecurityToken
+import com.normation.inventory.domain.Software
+import com.normation.inventory.services.core.FullInventoryRepository
+import com.normation.inventory.services.core.ReadOnlySoftwareNameDAO
+import com.normation.inventory.services.provisioning.PipelinedInventorySaver
+import com.normation.inventory.services.provisioning.PostCommit
+import com.normation.inventory.services.provisioning.PreCommit
+import com.normation.rudder.domain.nodes.Node
+import com.normation.rudder.domain.nodes.NodeInfo
+import com.normation.rudder.domain.nodes.NodeKind
+import com.normation.rudder.repository.WoNodeRepository
+import com.normation.rudder.services.nodes.NodeInfoService
+import com.softwaremill.quicklens._
+import org.joda.time.DateTime
+import zio._
+import zio.stream.ZSink
+import zio.syntax._
+
+class NodeFactInventorySaver(
+ backend: NodeFactRepository,
+ val preCommitPipeline: Seq[PreCommit],
+ val postCommitPipeline: Seq[PostCommit[Unit]]
+) extends PipelinedInventorySaver[Unit] {
+
+ override def commitChange(inventory: Inventory): IOResult[Unit] = {
+ implicit val cc = ChangeContext.newForRudder()
+ backend.updateInventory(FullInventory(inventory.node, Some(inventory.machine)), Some(inventory.applications)).unit
+ }
+}
+
+/*
+ * Proxy for node fact to full inventory / node inventory / machine inventory / node info and their repositories
+ */
+class NodeInfoServiceProxy(backend: NodeFactRepository) extends NodeInfoService {
+
+ override def getNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = {
+ backend.get(nodeId)(SelectNodeStatus.Accepted).map(_.map(_.toNodeInfo))
+ }
+
+ override def getNodeInfos(nodeIds: Set[NodeId]): IOResult[Set[NodeInfo]] = {
+ backend
+ .getAll()(SelectNodeStatus.Accepted)
+ .collect { case n if (nodeIds.contains(n.id)) => n.toNodeInfo }
+ .run(ZSink.collectAllToSet)
+ }
+
+ override def getNodeInfosSeq(nodeIds: Seq[NodeId]): IOResult[Seq[NodeInfo]] = {
+ backend
+ .getAll()(SelectNodeStatus.Accepted)
+ .collect { case n if (nodeIds.contains(n.id)) => n.toNodeInfo }
+ .run(ZSink.collectAll)
+ .map(_.toSeq)
+ }
+
+ override def getNumberOfManagedNodes: IOResult[Int] = {
+ backend.getAll()(SelectNodeStatus.Accepted).run(ZSink.count).map(_.toInt)
+ }
+
+ override def getAll(): IOResult[Map[NodeId, NodeInfo]] = {
+ backend.getAll()(SelectNodeStatus.Accepted).map(_.toNodeInfo) run (ZSink.collectAllToMap[NodeInfo, NodeId](_.node.id)(
+ (a, b) => b
+ ))
+ }
+
+ override def getAllNodesIds(): IOResult[Set[NodeId]] = {
+ backend.getAll()(SelectNodeStatus.Accepted).map(_.id).run(ZSink.collectAllToSet)
+ }
+
+ override def getAllNodes(): IOResult[Map[NodeId, Node]] = {
+ backend.getAll()(SelectNodeStatus.Accepted).map(_.toNode).run(ZSink.collectAllToMap[Node, NodeId](_.id)((a, b) => b))
+ }
+
+ override def getAllNodeInfos(): IOResult[Seq[NodeInfo]] = {
+ backend.getAll()(SelectNodeStatus.Accepted).map(_.toNodeInfo).run(ZSink.collectAll).map(_.toSeq)
+ }
+
+ override def getAllSystemNodeIds(): IOResult[Seq[NodeId]] = {
+ backend
+ .getAll()(SelectNodeStatus.Accepted)
+ .collect { case n if (n.rudderSettings.kind != NodeKind.Node) => n.id }
+ .run(ZSink.collectAll)
+ .map(_.toSeq)
+ }
+
+ override def getPendingNodeInfos(): IOResult[Map[NodeId, NodeInfo]] = {
+ backend.getAll()(SelectNodeStatus.Pending).map(_.toNodeInfo).run(ZSink.collectAllToMap[NodeInfo, NodeId](_.id)((a, b) => b))
+ }
+
+ override def getPendingNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = {
+ backend.get(nodeId)(SelectNodeStatus.Pending).map(_.map(_.toNodeInfo))
+ }
+
+ // not supported anymore
+ override def getDeletedNodeInfos(): IOResult[Map[NodeId, NodeInfo]] = {
+ Map().succeed
+ }
+
+ // not supported anymore
+ override def getDeletedNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = {
+ None.succeed
+ }
+}
+
+/*
+ * Proxy for full node inventory.
+ * We willfully chose to not implement machine repo because it doesn't make any sense with fact.
+ * There is also a limit with software, since now they are directly in the node and they don't
+ * have specific IDs. So they will need to be retrieved by node id.
+ */
+class NodeFactFullInventoryRepositoryProxy(backend: NodeFactRepository)
+ extends FullInventoryRepository[Unit] with ReadOnlySoftwareNameDAO {
+
+ override def get(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Option[FullInventory]] = {
+ backend.slowGetCompat(id, inventoryStatus, SelectFacts.noSoftware).map(_.map(_.toFullInventory))
+ }
+
+ override def get(id: NodeId): IOResult[Option[FullInventory]] = {
+ get(id, AcceptedInventory)
+ }
+
+ override def getMachineId(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Option[(MachineUuid, InventoryStatus)]] = {
+ (Some((NodeFact.toMachineId(id), inventoryStatus))).succeed
+ }
+
+ override def save(serverAndMachine: FullInventory): IOResult[Unit] = {
+ // we must know if it's new or not to get back the correct facts.
+ // if the fact exists, we keep its status (use move to change it).
+ // if it does not yet, we use the given status BUT know that you should not
+ // use that to save node in accepted status directly.
+ backend.updateInventory(serverAndMachine, None)(ChangeContext.newForRudder()).unit
+ }
+
+ override def delete(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Unit] = {
+ // we need to only delete if the status is the one asked
+ backend.getStatus(id).flatMap { s =>
+ s match {
+ case RemovedInventory => ZIO.unit
+ case s => ZIO.when(s == inventoryStatus)(backend.delete(id)(ChangeContext.newForRudder())).unit
+ }
+ }
+ }
+
+ override def move(id: NodeId, from: InventoryStatus, into: InventoryStatus): IOResult[Unit] = {
+ backend.changeStatus(id, into)(ChangeContext.newForRudder()).unit
+ }
+
+ override def moveNode(id: NodeId, from: InventoryStatus, into: InventoryStatus): IOResult[Unit] = {
+ backend.changeStatus(id, into)(ChangeContext.newForRudder()).unit
+ }
+
+ override def getSoftwareByNode(nodeIds: Set[NodeId], status: InventoryStatus): IOResult[Map[NodeId, Seq[Software]]] = {
+ def getAll(s: SelectNodeStatus): IOResult[Map[NodeId, Chunk[Software]]] = {
+ implicit val attrs = SelectFacts.none.copy(software = SelectFacts.all.software)
+
+ ZIO
+ .foreach(nodeIds.toList) {
+ case id =>
+ backend.slowGet(id)(s, attrs).map(_.map(n => (n.id, n.software.map(_.toSoftware))))
+ }
+ .map(_.flatten.toMap)
+ }
+
+ status match {
+ case AcceptedInventory => getAll(SelectNodeStatus.Accepted)
+ case PendingInventory => getAll(SelectNodeStatus.Pending)
+ case RemovedInventory => Map().succeed
+ }
+ }
+
+ override def getNodesbySofwareName(softName: String): IOResult[List[(NodeId, Software)]] = {
+ backend.getNodesbySofwareName(softName)
+ }
+}
+
+class WoFactNodeRepositoryProxy(backend: NodeFactRepository) extends WoNodeRepository {
+ override def updateNode(node: Node, modId: ModificationId, actor: EventActor, reason: Option[String]): IOResult[Node] = {
+ for {
+ opt <- backend.get(node.id)(SelectNodeStatus.Any)
+ fact <- opt match {
+ case None => Inconsistency(s"Node with id '${node.id.value}' was not found").fail
+ case Some(fact) => CoreNodeFact.updateNode(fact, node).succeed
+ }
+ _ <- backend.save(NodeFact.fromMinimal(fact))(ChangeContext(modId, actor, DateTime.now(), reason, None), SelectFacts.none)
+ } yield fact.toNode
+ }
+
+ override def deleteNode(node: Node, modId: ModificationId, actor: EventActor, reason: Option[String]): IOResult[Node] = ???
+
+ override def createNode(node: Node, modId: ModificationId, actor: EventActor, reason: Option[String]): IOResult[Node] = ???
+
+ override def updateNodeKeyInfo(
+ nodeId: NodeId,
+ agentKey: Option[SecurityToken],
+ agentKeyStatus: Option[KeyStatus],
+ modId: ModificationId,
+ actor: EventActor,
+ reason: Option[String]
+ ): IOResult[Unit] = {
+ if (agentKey.isEmpty && agentKeyStatus.isEmpty) ZIO.unit
+ else {
+ for {
+ _ <- agentKey match {
+ case Some(Certificate(value)) => SecurityToken.checkCertificateForNode(nodeId, Certificate(value))
+ case _ => ZIO.unit
+ }
+ node <- backend.get(nodeId).notOptional(s"Cannot update node with id ${nodeId.value}: there is no node with that id")
+ newNode = node
+ .modify(_.rudderAgent.securityToken)
+ .setToIfDefined(agentKey)
+ .modify(_.rudderSettings.keyStatus)
+ .setToIfDefined(agentKeyStatus)
+ _ <-
+ backend.save(NodeFact.fromMinimal(newNode))(ChangeContext(modId, actor, DateTime.now(), reason, None), SelectFacts.none)
+ } yield ()
+ }
+ }
+}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactStorage.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactStorage.scala
new file mode 100644
index 00000000000..8b4e896cd6a
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/facts/nodes/NodeFactStorage.scala
@@ -0,0 +1,911 @@
+/*
+ *************************************************************************************
+ * Copyright 2021 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.facts.nodes
+
+import NodeFactSerialisation._
+import better.files.File
+import com.normation.errors._
+import com.normation.errors.IOResult
+import com.normation.inventory.domain._
+import com.normation.inventory.ldap.core.FullInventoryRepositoryImpl
+import com.normation.inventory.ldap.core.InventoryDitService
+import com.normation.inventory.ldap.core.InventoryMapper
+import com.normation.inventory.ldap.core.LDAPConstants._
+import com.normation.inventory.services.core.ReadOnlySoftwareDAO
+import com.normation.inventory.services.provisioning.SoftwareDNFinderAction
+import com.normation.ldap.sdk.BuildFilter._
+import com.normation.ldap.sdk.LDAPConnectionProvider
+import com.normation.ldap.sdk.LDAPEntry
+import com.normation.ldap.sdk.One
+import com.normation.ldap.sdk.RwLDAPConnection
+import com.normation.rudder.domain.NodeDit
+import com.normation.rudder.domain.logger.NodeLogger
+import com.normation.rudder.domain.logger.NodeLoggerPure
+import com.normation.rudder.domain.nodes.MachineInfo
+import com.normation.rudder.domain.nodes.NodeInfo
+import com.normation.rudder.facts.nodes.LdapNodeFactStorage.needsSoftware
+import com.normation.rudder.git.GitItemRepository
+import com.normation.rudder.git.GitRepositoryProvider
+import com.normation.rudder.repository.ldap.LDAPEntityMapper
+import com.normation.rudder.repository.ldap.ScalaReadWriteLock
+import com.normation.rudder.services.nodes.NodeInfoService
+import com.normation.utils.StringUuidGenerator
+import com.normation.zio._
+import com.softwaremill.quicklens._
+import com.unboundid.ldap.sdk.DN
+import java.nio.charset.StandardCharsets
+import org.eclipse.jgit.lib.PersonIdent
+import org.joda.time.DateTime
+import scala.annotation.nowarn
+import zio._
+import zio.json._
+import zio.stream.ZStream
+import zio.syntax._
+
+/*
+ * This file contains the base to persist facts into a git repository. There is a lot of question
+ * remaining, so don't take current traits/classes as an API, it *WILL* change. The basic questions to answer are:
+ * - do we want one bit "FactRepo" that knows about all kind of facts and is able to persis any of them ? In that case,
+ * we will need some kind of parametrization of `persist` with a type class to teach that repo how to serialize and
+ * persist each case
+ * - do we prefer lots of small repos, one by entity, which knows how to persist only that entity ?
+ * - plus, we want to have some latitude on the serialization part, and be able to use both liftjson and zio-json
+ * (because the complete migration toward zio-json won't be finish immediately)
+ *
+ * The "one big" repo feels more like it is what we need, since it's really just one big git repo with sub-cases,
+ * with shared tools and specialisation by entity. But I'm not sure how to build the capacities with type class
+ * until I have several examples.
+ * The small repos (one by entity) is what we used to do, so we are in known territory (see archive of configuration
+ * entities), even if it is not a very satisfying one. Its advantage is that it's very simple, but it leads to a lot
+ * of code duplication and maintenance is complicated (and adding a new entity is basically "copy that 100 lines of
+ * code, and sed things", while we would like it to be "implement just that interface")
+ *
+ * Finally, we some coupling between serialization and repos as they are written for now: the path can't be known
+ * without some part of the entity, but we don't know which part exactly (for node, it's its uuid and status, but
+ * perhaps it's an exception, and for all other it's just an ID).
+ *
+ * With all these unknowns, I prefer to let parametrisation as simple as possible:
+ * - no abstraction for repo, we just have a "node repo" with all the concrete types. It's likely to become a:
+ * ```
+ * trait FactRepo { def persist[E](e: E)(implicit Serialize[E]): IOResult[Unit])
+ * ```
+ * Or something alike, but we just don't know.
+ *
+ * - some abstraction for serialisation, but just to put in plain sight the fact that there a characteristic of
+ * the entity that is not the whole entity, and more then its ID, that is needed to build where the entity
+ * will be saved.
+ *
+ * - a simple implementation for nodes, that will need to be refactored depending of the chosen final arch.
+ *
+ * And finally, to complexity a bit more the picture, we see that there is events (observations?) linked to facts
+ * that can update the previous fact partially. For nodes, it's "change the status" (which is, perhaps by luck,
+ * the same subpart of the entity than the one used in the more-than-just-an-id parameter of serialization).
+ * I don't know for now if it's a general truth, or if it's just an happenstance, and if there is a general
+ * capability (like "partialUpdate[SomeSubParOfE => E]") to define (in a pure events-tore, we would save that
+ * event as if, but well we want to have readable text files for users in our git repos)
+ */
+
+/*
+ * Serialize a fact type (to/from JSON), for example nodes.
+ * The format is versioned so that we are able to unserialize old files into newer domain representation.
+ *
+ * We store a fileFormat and the serialized object type.
+ * To let more space for evolution, file format will be a string even if it should be parsed as an int.
+ *
+ * There's two parameter, one minimal (A) that allows to identify where the fact should be store (typically, it's a
+ * kind of ID), and (B) which the whole fact to serialize. There should exists a constraint of derivability from B to A,
+ * but it's not modeled.
+ */
+trait SerializeFacts[A, B] {
+
+ def fileFormat: String
+ def entity: String
+
+ def toJson(data: B): IOResult[String]
+
+ // this is just a relative path from a virtual root, for example for node it will be: "accepted/node-uuid.json"
+ def getEntityPath(id: A): String
+
+}
+
+/*
+ * Storage have a very simple change event output datastructure. This is because they don't know
+ * about business logic, just about the action done to the serialized data.
+ *
+ * SelectFacts informs about what the storage know about the change.
+ * For ex, if SelectFacts.software is "Ignore" and the change event is Noop, it only
+ * says that things other than software changed and nothing about software (it may or may
+ * not have changed).
+ * This is also a way to limit the quantity of data returned in the change event: NodeFact
+ * will (must be) masked. Finally, storage must not change the attrs requirement given in
+ * parameter and must do whatever is needed to honor the requirement.
+ *
+ * When no requirement is done in the parameters for SelectFacts, storage can return
+ * what it wants, always trying to maximize perf / minimize data transfered in that case .
+ */
+sealed trait StorageChangeEvent
+// save
+sealed trait StorageChangeEventSave extends StorageChangeEvent
+object StorageChangeEventSave {
+ final case class Created(node: NodeFact, attrs: SelectFacts) extends StorageChangeEventSave
+ final case class Updated(oldNode: NodeFact, newNode: NodeFact, attrs: SelectFacts) extends StorageChangeEventSave
+ final case class Noop(nodeId: NodeId, attrs: SelectFacts) extends StorageChangeEventSave
+
+ implicit class StorageChangeEventExtensions(e: StorageChangeEventSave) {
+ def toChangeEvent(
+ nodeId: NodeId,
+ s: InventoryStatus,
+ cc: ChangeContext
+ ): NodeFactChangeEventCC = {
+ import NodeFactChangeEvent.{Noop => NoopCE}
+ import NodeFactChangeEvent.{Updated => UpdatedCE, _}
+ s match {
+ case RemovedInventory => // this case is ignored, we don't delete node based on status value
+ NodeFactChangeEventCC(NoopCE(nodeId, SelectFacts.all), cc)
+ case PendingInventory =>
+ e match {
+ case StorageChangeEventSave.Created(node, attrs) => NodeFactChangeEventCC(NewPending(node, attrs), cc)
+ case StorageChangeEventSave.Updated(old, next, attrs) => NodeFactChangeEventCC(UpdatedPending(old, next, attrs), cc)
+ case StorageChangeEventSave.Noop(nodeId, attrs) => NodeFactChangeEventCC(NoopCE(nodeId, attrs), cc)
+ }
+ case AcceptedInventory =>
+ e match {
+ case StorageChangeEventSave.Created(node, attrs) => NodeFactChangeEventCC(Accepted(node, attrs), cc)
+ case StorageChangeEventSave.Updated(old, next, attrs) => NodeFactChangeEventCC(UpdatedCE(old, next, attrs), cc)
+ case StorageChangeEventSave.Noop(nodeId, attrs) => NodeFactChangeEventCC(NoopCE(nodeId, attrs), cc)
+ }
+ }
+ }
+
+ def updateWith(b: StorageChangeEventSave): StorageChangeEventSave = {
+ def update(nfa: NodeFact, nfb: NodeFact, attrs: SelectFacts) = SelectFacts.merge(nfa, Some(nfb))(attrs.invert)
+
+ (e, b) match {
+ case (Noop(_, _), x) => x
+ case (x, Noop(_, _)) => x
+ case (Created(a1, _), Created(a2, attrs)) => Created(update(a1, a2, attrs), attrs)
+ case (Created(a1, _), Updated(ob, nb, attrs)) => Updated(ob, update(a1, nb, attrs), attrs)
+ case (Updated(oa, na, _), Created(b1, attrs)) => Updated(oa, update(na, b1, attrs), attrs)
+ case (Updated(ob1, nb1, _), Updated(ob2, nb2, attrs)) =>
+ Updated(
+ update(ob1, ob2, attrs),
+ update(nb1, nb1, attrs),
+ attrs
+ )
+ }
+ }
+ }
+}
+
+sealed trait StorageChangeEventStatus extends StorageChangeEvent
+object StorageChangeEventStatus {
+ final case class Done(nodeId: NodeId) extends StorageChangeEventStatus
+ final case class Noop(nodeId: NodeId) extends StorageChangeEventStatus
+}
+
+sealed trait StorageChangeEventDelete extends StorageChangeEvent
+object StorageChangeEventDelete {
+ final case class Deleted(node: NodeFact, attrs: SelectFacts) extends StorageChangeEventDelete
+ // this one is to signal something was deleted, but we weren't able to retrieve node info
+ final case class DeletedNoInfo(nodeId: NodeId) extends StorageChangeEventDelete
+ final case class Noop(nodeId: NodeId) extends StorageChangeEventDelete
+
+ implicit class StorageChangeEventExtensions(e: StorageChangeEventDelete) {
+ def toChangeEvent(
+ cnf: CoreNodeFact,
+ s: InventoryStatus,
+ cc: ChangeContext
+ ): NodeFactChangeEventCC = {
+ import NodeFactChangeEvent.{Noop => NoopCE, Deleted => DeletedCE, _}
+
+ def patternMatch(
+ event: StorageChangeEventDelete,
+ toEvent: (NodeFact, SelectFacts) => NodeFactChangeEvent
+ ): NodeFactChangeEventCC = {
+ event match {
+ case Deleted(node, attrs) => NodeFactChangeEventCC(toEvent(node, attrs), cc)
+ case DeletedNoInfo(_) => NodeFactChangeEventCC(toEvent(NodeFact.fromMinimal(cnf), SelectFacts.none), cc)
+ case Noop(_) => NodeFactChangeEventCC(NoopCE(cnf.id, SelectFacts.all), cc)
+ }
+ }
+
+ s match {
+ case RemovedInventory => NodeFactChangeEventCC(NoopCE(cnf.id, SelectFacts.all), cc)
+ case PendingInventory => patternMatch(e, Refused(_, _))
+ case AcceptedInventory => patternMatch(e, DeletedCE(_, _))
+ }
+ }
+
+ def updateWith(b: StorageChangeEventDelete): StorageChangeEventDelete = {
+ def update(nfa: NodeFact, nfb: NodeFact, attrs: SelectFacts) = SelectFacts.merge(nfa, Some(nfb))(attrs.invert)
+
+ (e, b) match {
+ case (Noop(_), x) => x
+ case (x, Noop(_)) => x
+ case (DeletedNoInfo(_), x) => x
+ case (x, DeletedNoInfo(_)) => x
+ case (Deleted(a1, _), Deleted(a2, attrs)) => Deleted(update(a1, a2, attrs), attrs)
+ }
+ }
+ }
+}
+
+trait NodeFactStorage {
+
+ /*
+ * Save node fact in the status given in the corresponding attribute.
+ * No check will be done.
+ */
+ def save(nodeFact: NodeFact)(implicit attrs: SelectFacts = SelectFacts.all): IOResult[StorageChangeEventSave]
+
+ /*
+ * Change the status of the node with given id to given status.
+ * - if the node is not found, an error is raised apart if target status is "delete"
+ * - if the target status is the current one, this function does nothing
+ * - if target status is "removed", persisted inventory is deleted
+ */
+ def changeStatus(nodeId: NodeId, status: InventoryStatus): IOResult[StorageChangeEventStatus]
+
+ /*
+ * Delete the node. Storage need to loop for any status and delete
+ * any reference to that node.
+ */
+ def delete(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[StorageChangeEventDelete]
+
+ def getPending(nodeId: NodeId)(implicit attrs: SelectFacts = SelectFacts.default): IOResult[Option[NodeFact]]
+ def getAccepted(nodeId: NodeId)(implicit attrs: SelectFacts = SelectFacts.default): IOResult[Option[NodeFact]]
+
+ def getAllPending()(implicit attrs: SelectFacts = SelectFacts.default): IOStream[NodeFact]
+ def getAllAccepted()(implicit attrs: SelectFacts = SelectFacts.default): IOStream[NodeFact]
+}
+
+/*
+ * Implementation that store nothing and that can be used in tests or when a pure
+ * in-memory version of the nodeFactRepos is needed.
+ */
+object NoopFactStorage extends NodeFactStorage {
+ override def save(nodeFact: NodeFact)(implicit attrs: SelectFacts = SelectFacts.default): IOResult[StorageChangeEventSave] =
+ StorageChangeEventSave.Noop(nodeFact.id, attrs).succeed
+ override def changeStatus(nodeId: NodeId, status: InventoryStatus): IOResult[StorageChangeEventStatus] =
+ StorageChangeEventStatus.Noop(nodeId).succeed
+ override def delete(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[StorageChangeEventDelete] =
+ StorageChangeEventDelete.Noop(nodeId).succeed
+ @nowarn("msg=parameter attrs in method getAllPending is never used")
+ override def getAllPending()(implicit attrs: SelectFacts = SelectFacts.default): IOStream[NodeFact] = ZStream.empty
+ @nowarn("msg=parameter attrs in method getAllAccepted is never used")
+ override def getAllAccepted()(implicit attrs: SelectFacts = SelectFacts.default): IOStream[NodeFact] = ZStream.empty
+ override def getPending(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = None.succeed
+ override def getAccepted(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = None.succeed
+}
+
+/*
+ * We have only one git for all fact repositories. This is the one managing semaphore, init, etc.
+ * All fact repositories will be a subfolder on it:
+ * - /var/rudder/fact-repository/nodes
+ * - /var/rudder/fact-repository/rudder-config
+ * - /var/rudder/fact-repository/groups
+ * etc
+ */
+
+object GitNodeFactStorageImpl {
+
+ final case class NodeFactArchive(
+ entity: String,
+ fileFormat: String,
+ node: NodeFact
+ )
+
+ implicit val codecNodeFactArchive: JsonCodec[NodeFactArchive] = DeriveJsonCodec.gen
+}
+
+/*
+ * Nodes are stored in the git facts repo under the relative path "nodes".
+ * They are then stored:
+ * - under nodes/pending or nodes/accepted given their status (which means that changing status of a node is
+ * a special operation)
+ */
+class GitNodeFactStorageImpl(
+ override val gitRepo: GitRepositoryProvider,
+ groupOwner: Option[String],
+ actuallyCommit: Boolean
+) extends NodeFactStorage with GitItemRepository with SerializeFacts[(NodeId, InventoryStatus), NodeFact] {
+
+ override val relativePath = "nodes"
+ override val entity: String = "node"
+ override val fileFormat: String = "10"
+ val committer = new PersonIdent("rudder-fact", "email not set")
+
+ if (actuallyCommit) {
+ NodeLogger.info(s"Nodes changes will be historized in Git in ${gitRepo.rootDirectory.pathAsString}/nodes")
+ } else {
+ NodeLogger.info(
+ s"Nodes changes won't be historized in Git, only last state is stored in ${gitRepo.rootDirectory.pathAsString}/nodes"
+ )
+ }
+
+ if (actuallyCommit) {
+ NodeLogger.info(s"Nodes changes will be historized in Git in ${gitRepo.rootDirectory.pathAsString}/nodes")
+ } else {
+ NodeLogger.info(
+ s"Nodes changes won't be historized in Git, only last state is stored in ${gitRepo.rootDirectory.pathAsString}/nodes"
+ )
+ }
+
+ override def getEntityPath(id: (NodeId, InventoryStatus)): String = {
+ s"${id._2.name}/${id._1.value}.json"
+ }
+
+ def getFile(id: NodeId, status: InventoryStatus): File = {
+ gitRepo.rootDirectory / relativePath / getEntityPath((id, status))
+ }
+
+ /*
+ * serialize the inventory into a normalized JSON string.
+ * As we want it to be human readable and searchable, we will use an indented format.
+ */
+ def toJson(nodeFact: NodeFact): IOResult[String] = {
+ import GitNodeFactStorageImpl._
+ val node = nodeFact
+ .modify(_.accounts)
+ .using(_.sorted)
+ .modify(_.properties)
+ .using(_.sortBy(_.name))
+ .modify(_.environmentVariables)
+ .using(_.sortBy(_._1))
+ .modify(_.fileSystems)
+ .using(_.sortBy(_.name))
+ .modify(_.networks)
+ .using(_.sortBy(_.name))
+ .modify(_.processes)
+ .using(_.sortBy(_.commandName))
+ .modify(_.bios)
+ .using(_.sortBy(_.name))
+ .modify(_.controllers)
+ .using(_.sortBy(_.name))
+ .modify(_.memories)
+ .using(_.sortBy(_.name))
+ .modify(_.ports)
+ .using(_.sortBy(_.name))
+ .modify(_.processors)
+ .using(_.sortBy(_.name))
+ .modify(_.slots)
+ .using(_.sortBy(_.name))
+ .modify(_.sounds)
+ .using(_.sortBy(_.name))
+ .modify(_.storages)
+ .using(_.sortBy(_.name))
+ .modify(_.videos)
+ .using(_.sortBy(_.name))
+
+ NodeFactArchive(entity, fileFormat, node).toJsonPretty.succeed
+ }
+
+ // we don't want to write the codec to "not unserialize" given SelectFacts right now, so we are
+ // just masking
+ private[nodes] def fileToNode(f: File)(implicit attrs: SelectFacts): IOResult[NodeFact] = {
+ for {
+ c <- IOResult.attempt(s"Error reading file: ${f.pathAsString}")(f.contentAsString(StandardCharsets.UTF_8))
+ j <- c.fromJson[NodeFact].toIO.chainError(s"Error when decoding ${f.pathAsString}")
+ } yield {
+ j.maskWith(attrs)
+ }
+ }
+
+ private[nodes] def get(nodeId: NodeId, status: InventoryStatus)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ val f = getFile(nodeId, status)
+ if (f.exists) fileToNode(f).map(Some(_))
+ else None.succeed
+ }
+
+ private[nodes] def getAll(base: File)(implicit attrs: SelectFacts): IOStream[NodeFact] = {
+ // TODO should be from git head, not from file directory
+ val stream = ZStream.fromIterator(base.collectChildren(_.extension(includeDot = true, includeAll = true) == Some(".json")))
+ stream
+ .mapError(ex => SystemError("Error when reading node fact persisted file", ex))
+ .mapZIO(f => fileToNode(f))
+ }
+
+ override def getPending(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ get(nodeId, PendingInventory)
+ }
+
+ override def getAccepted(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ get(nodeId, AcceptedInventory)
+ }
+
+ override def getAllPending()(implicit attrs: SelectFacts): IOStream[NodeFact] = getAll(
+ gitRepo.rootDirectory / relativePath / PendingInventory.name
+ )
+ override def getAllAccepted()(implicit attrs: SelectFacts): IOStream[NodeFact] = getAll(
+ gitRepo.rootDirectory / relativePath / AcceptedInventory.name
+ )
+
+ // We saving, we must ignore attrs that are "ignored" - ie in that case, if the source list is empty, we take the existing one
+ // Save does not know about status change. If it's called with a status change, this leads to duplicated data.
+ override def save(nodeFact: NodeFact)(implicit attrs: SelectFacts): IOResult[StorageChangeEventSave] = {
+ if (nodeFact.rudderSettings.status == RemovedInventory) {
+ InventoryDataLogger.info(
+ s"Not persisting deleted node '${nodeFact.fqdn}' [${nodeFact.id.value}]: it has removed inventory status"
+ ) *> StorageChangeEventSave.Noop(nodeFact.id, attrs).succeed
+ } else {
+ val file = getFile(nodeFact.id, nodeFact.rudderSettings.status)
+ for {
+ old <- fileToNode(file).map(Some(_)).catchAll(_ => None.succeed)
+ merged = SelectFacts.merge(nodeFact, old)
+ json <- toJson(merged)
+ _ <- IOResult.attempt(file.write(json))
+ _ <- groupOwner match {
+ case None => ZIO.unit
+ case Some(go) => IOResult.attempt(file.setGroup(go))
+ }
+ _ <- ZIO.when(actuallyCommit) {
+ commitAddFile(
+ committer,
+ toGitPath(file.toJava),
+ s"Save inventory facts for ${merged.rudderSettings.status.name} node '${merged.fqdn}' (${merged.id.value})"
+ )
+ }
+ } yield {
+ old match {
+ case Some(o) =>
+ StorageChangeEventSave.Updated(o, nodeFact, attrs)
+ case None =>
+ StorageChangeEventSave.Created(nodeFact, attrs)
+ }
+ }
+ }
+ }
+
+ // when we delete, we check for all path to also remove possible left-over
+ // we may need to recreate pending/accepted directory, because git delete
+ // empty directories.
+ override def delete(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[StorageChangeEventDelete] = {
+ def exists(nodeId: NodeId, status: InventoryStatus): IOResult[Option[File]] = {
+ val file = getFile(nodeId, status)
+ IOResult.attempt(file.exists).map {
+ case true => Some(file)
+ case false => None
+ }
+ }
+ def delete(file: File) = {
+ (if (actuallyCommit) {
+ commitRmFile(committer, toGitPath(file.toJava), s"Updating facts for node '${nodeId.value}': deleted")
+ } else {
+ IOResult.attempt(file.delete())
+ }).flatMap(_ => fileToNode(file)(attrs).map(Some(_)).catchAll(_ => None.succeed)).map {
+ case Some(n) => StorageChangeEventDelete.Deleted(n, attrs)
+ case None => StorageChangeEventDelete.Noop(nodeId)
+ }
+ }
+
+ (for {
+ p <- exists(nodeId, PendingInventory)
+ a <- exists(nodeId, AcceptedInventory)
+ c <- (p, a) match {
+ case (None, None) => StorageChangeEventDelete.Noop(nodeId).succeed
+ case (_, Some(f)) => delete(f)
+ case (Some(f), _) => delete(f)
+ }
+ } yield c)
+ .tap(_ => checkInit())
+ }
+
+ override def changeStatus(nodeId: NodeId, toStatus: InventoryStatus): IOResult[StorageChangeEventStatus] = {
+ // pending and accepted are symmetric, utility function for the two cases
+ def move(to: InventoryStatus): IOResult[StorageChangeEventStatus] = {
+ val from = if (to == AcceptedInventory) PendingInventory else AcceptedInventory
+
+ val fromFile = getFile(nodeId, from)
+ val toFile = getFile(nodeId, to)
+ // check if fact already where it should
+ ZIO.ifZIO(IOResult.attempt(fromFile.exists))(
+ // however toFile exists, move, because if present it may be because a deletion didn't work and
+ // we need to overwrite
+ IOResult.attempt(fromFile.moveTo(toFile)(File.CopyOptions(overwrite = true))) *>
+ ZIO.when(actuallyCommit) {
+ commitMvDirectory(
+ committer,
+ toGitPath(fromFile.toJava),
+ toGitPath(toFile.toJava),
+ s"Updating facts for node '${nodeId.value}' to status: ${to.name}"
+ )
+ } *> fileToNode(toFile)(SelectFacts.none).map(Some(_)).catchAll(_ => None.succeed).map {
+ case Some(n) => StorageChangeEventStatus.Done(n.id)
+ case None => StorageChangeEventStatus.Noop(nodeId)
+ },
+ // if source file does not exist, check if dest is present. If present, assume it's ok, else error
+ ZIO
+ .whenZIO(IOResult.attempt(!toFile.exists)) {
+ Inconsistency(
+ s"Error when trying to move fact for node '${nodeId.value}' from '${fromFile.pathAsString}' to '${toFile.pathAsString}': missing files"
+ ).fail
+ }
+ .map(_ => StorageChangeEventStatus.Noop(nodeId))
+ )
+ }
+
+ toStatus match {
+ case RemovedInventory =>
+ delete(nodeId)(SelectFacts.none).map {
+ case StorageChangeEventDelete.Deleted(node, attrs) => StorageChangeEventStatus.Done(nodeId)
+ case StorageChangeEventDelete.DeletedNoInfo(node) => StorageChangeEventStatus.Done(nodeId)
+ case StorageChangeEventDelete.Noop(nodeId) => StorageChangeEventStatus.Noop(nodeId)
+ }
+ case x => move(x)
+ }
+ }
+
+ /*
+ * check that everything is ok for that repo entities (typically: subfolder created, perm ok, etc)
+ */
+ def checkInit(): IOResult[Unit] = {
+ val dirs = List(AcceptedInventory.name, PendingInventory.name)
+ dirs.accumulate { dir =>
+ val d = gitRepo.rootDirectory / relativePath / dir
+ for {
+ _ <- ZIO
+ .whenZIO(IOResult.attempt(d.notExists)) {
+ IOResult.attempt {
+ d.createDirectories()
+ groupOwner.map(go => d.setGroup(go))
+ }
+ }
+ .chainError(s"Error when creating directory '${d.pathAsString}' for historising inventories: ${}")
+ _ <- ZIO.whenZIO(IOResult.attempt(!d.isOwnerWritable)) {
+ Inconsistency(
+ s"Error, directory '${d.pathAsString}' must be a writable directory to allow inventory historisation"
+ ).fail
+ }
+ } yield ()
+ }.unit
+ }
+}
+
+/*
+ * An LDAP implementation for NodeFactStorage.
+ * It takes most of its code from old FullLdapInventory/NodeInfoService
+ */
+
+object LdapNodeFactStorage {
+
+ /*
+ * We have 3 main places where facts can be stored:
+ * - in ou=Nodes,cn=rudder-configuration
+ * for settings, properties, state, etc
+ * - in ou=[Nodes, Machines],ou=[Accepted|Pengin] Inventories,ou=Inventories,cn=rudder-configuration
+ * (and sub entries) for os, ram, swap, machine type, CPU, etc
+ * The mapping is very complicated, and we just want to reuse inventory repository for that
+ * - in ou=Software, ou=Inventories,cn=rudder-configuration
+ * For software.
+ *
+ * So we need for each element of SelectFact to know what part of entries it need
+ */
+
+ def needsSoftware(selectFacts: SelectFacts): Boolean = {
+ selectFacts.software.mode == SelectMode.Retrieve
+ }
+
+ def inventoryFacts(s: SelectFacts) = {
+ List(
+ s.swap,
+ s.accounts,
+ s.bios,
+ s.controllers,
+ s.environmentVariables,
+ s.fileSystems,
+ s.inputs,
+ s.localGroups,
+ s.localUsers,
+ s.logicalVolumes,
+ s.memories,
+ s.networks,
+ s.physicalVolumes,
+ s.ports,
+ s.processes,
+ s.processors,
+ s.slots,
+ s.softwareUpdate,
+ s.sounds,
+ s.storages,
+ s.videos,
+ s.vms
+ )
+ }
+
+ def needsInventory(selectFacts: SelectFacts): Boolean = {
+ inventoryFacts(selectFacts).exists(_.mode == SelectMode.Retrieve)
+ }
+
+}
+
+class LdapNodeFactStorage(
+ ldap: LDAPConnectionProvider[RwLDAPConnection],
+ nodeDit: NodeDit,
+ inventoryDitService: InventoryDitService,
+ nodeMapper: LDAPEntityMapper,
+ inventoryMapper: InventoryMapper,
+ nodeLibMutex: ScalaReadWriteLock, // that's a scala-level mutex to have some kind of consistency with LDAP
+ fullInventoryRepository: FullInventoryRepositoryImpl,
+ softwareGet: ReadOnlySoftwareDAO,
+ softwareSave: SoftwareDNFinderAction,
+ uuidGen: StringUuidGenerator
+) extends NodeFactStorage {
+
+ // for save, we always store the full node. Since we don't know how to restrict attributes to save
+ // for the inventory part (node part is always complete), we do retrieve then merge, avoiding software if possible
+ override def save(nodeFact: NodeFact)(implicit attrs: SelectFacts): IOResult[StorageChangeEventSave] = {
+ nodeLibMutex.writeLock(for {
+ con <- ldap
+ _ <- con
+ .save(nodeMapper.nodeToEntry(nodeFact.toNode))
+ .chainError(s"Cannot save node with id '${nodeFact.id.value}' in LDAP")
+ mergedSoft <- if (LdapNodeFactStorage.needsSoftware(attrs)) {
+ softwareSave.tryWith(nodeFact.software.map(_.toSoftware).toSet).map(Some(_))
+ } else None.succeed
+ newSoftIds <- mergedSoft match {
+ case None => None.succeed
+ case Some(merged) =>
+ val newSoft = merged.newSoftware.toSeq.map(_.modify(_.id.value).setTo(uuidGen.newUuid))
+ for {
+ _ <- ZIO.foreach(newSoft)(x => con.save(inventoryMapper.entryFromSoftware(x)))
+ } yield Some(newSoft.map(_.id) ++ merged.alreadySavedSoftware.map(_.id))
+ }
+ optOld <- getNodeFact(nodeFact.id, nodeFact.rudderSettings.status, SelectFacts.noSoftware).map(
+ _.map { nf =>
+ // add back software if needed
+ val optSoft = if (LdapNodeFactStorage.needsSoftware(attrs)) {
+ mergedSoft.map(m => Chunk.fromIterable(m.alreadySavedSoftware).flatMap(s => SoftwareFact.fromSoftware(s)))
+ } else {
+ None
+ }
+ nf.modify(_.software).setToIfDefined(optSoft)
+ }
+ )
+ inv = SelectFacts
+ .merge(nodeFact, optOld)(attrs)
+ .toFullInventory
+ .modify(_.node.softwareIds)
+ .setToIfDefined(newSoftIds)
+ _ <- fullInventoryRepository.save(inv)
+ } yield {
+ optOld match {
+ case Some(old) =>
+ if (NodeFact.same(old, nodeFact)) StorageChangeEventSave.Noop(nodeFact.id, attrs)
+ else StorageChangeEventSave.Updated(old, nodeFact, attrs)
+ case None =>
+ StorageChangeEventSave.Created(nodeFact, attrs)
+ }
+ })
+ }
+
+ override def changeStatus(nodeId: NodeId, status: InventoryStatus): IOResult[StorageChangeEventStatus] = {
+ for {
+ s <- fullInventoryRepository.getStatus(nodeId).notOptional(s"Error: node with ID '${nodeId.value}' was not found'")
+ _ <- if (s == status) ZIO.unit
+ else if (s == RemovedInventory) {
+ Inconsistency(
+ s"Error: node with ID '${nodeId.value}' is deleted, can not change its status to '${status.name}''"
+ ).fail
+ } else fullInventoryRepository.move(nodeId, s, status)
+ } yield StorageChangeEventStatus.Done(nodeId)
+ }
+
+ override def delete(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[StorageChangeEventDelete] = {
+ def cleanLdapData(): UIO[Boolean] = {
+ val cleanNode = (
+ for {
+ con <- ldap
+ d1 <- nodeLibMutex.writeLock(con.delete(nodeDit.NODES.NODE.dn(nodeId.value)))
+ } yield d1.nonEmpty
+ ).catchAll { err =>
+ NodeLoggerPure.Delete.error(
+ s"Error when deleting node main information in LDAP, please try to call the delete API with " +
+ s"node ID parameter: ${nodeId.value} to correct it; Error: ${err.fullMsg}"
+ ) *> true.succeed // there was things in LDAP, they may have changed
+ }
+
+ def cleanInventory(s: InventoryStatus): UIO[Boolean] = {
+ fullInventoryRepository.delete(nodeId, s).map(_.nonEmpty).catchAll { err =>
+ NodeLoggerPure.Delete.error(
+ s"Error when deleting node inventory information in LDAP '${s.name}', please try to call the delete API with " +
+ s"node ID parameter: ${nodeId.value} to correct it; Error: ${err.fullMsg}"
+ ) *> true.succeed
+ }
+ }
+
+ ZIO
+ .foreachPar(List(cleanNode, cleanInventory(PendingInventory), cleanInventory(AcceptedInventory)))(identity)
+ .map(_.exists(_ == true))
+ }
+
+ for {
+ // get information for change event
+ p <- getNodeFact(nodeId, PendingInventory, attrs)
+ a <- getNodeFact(nodeId, AcceptedInventory, attrs)
+ // in all case, delete everywhere
+ mod <- cleanLdapData()
+ } yield {
+ (p, a) match {
+ case (_, Some(x)) =>
+ StorageChangeEventDelete.Deleted(x, attrs)
+ case (Some(x), None) =>
+ StorageChangeEventDelete.Deleted(x, attrs)
+ case (None, None) =>
+ if (mod) {
+ StorageChangeEventDelete.Noop(nodeId)
+ } else {
+ StorageChangeEventDelete.DeletedNoInfo(nodeId)
+ }
+ }
+ }
+ }
+
+ /*
+ * Get node fact with trying to make the minimum data retieval from ldap (the granularity is coarse: we only check if
+ * we need full inventory or just node info, and software or not)
+ */
+ private[nodes] def getNodeFact(nodeId: NodeId, status: InventoryStatus, attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+
+ def getNodeEntry(con: RwLDAPConnection, id: NodeId): IOResult[Option[LDAPEntry]] = {
+ con.get(nodeDit.NODES.NODE.dn(nodeId.value), NodeInfoService.nodeInfoAttributes: _*)
+ }
+ def getSoftware(
+ con: RwLDAPConnection,
+ ids: Seq[SoftwareUuid],
+ needSoftware: Boolean
+ ): IOResult[Seq[Software]] = {
+ if (needSoftware && ids.nonEmpty) {
+ softwareGet.getSoftware(ids)
+ } else Seq().succeed
+ }
+ def getFromFullInventory(
+ con: RwLDAPConnection,
+ nodeId: NodeId,
+ nodeEntry: LDAPEntry,
+ needSoftware: Boolean
+ ): IOResult[Option[NodeFact]] = {
+ for {
+ node <- nodeMapper.entryToNode(nodeEntry).toIO
+ optInvS <- fullInventoryRepository.getWithSoftware(nodeId, status, needSoftware)
+ softs <- getSoftware(con, optInvS.map(_._2).getOrElse(Seq()), needSoftware)
+ } yield {
+ optInvS.map {
+ case (inv, _) =>
+ val info = NodeInfo(
+ node,
+ inv.node.main.hostname,
+ inv.machine.map(m => MachineInfo(m.id, m.machineType, m.systemSerialNumber, m.manufacturer)),
+ inv.node.main.osDetails,
+ inv.node.serverIps.toList,
+ inv.node.inventoryDate.getOrElse(DateTime.now),
+ inv.node.main.keyStatus,
+ inv.node.agents,
+ inv.node.main.policyServerId,
+ inv.node.main.rootUser,
+ inv.node.archDescription,
+ inv.node.ram,
+ inv.node.timezone
+ )
+ NodeFact.fromCompat(info, Right(inv), softs)
+ }
+ }
+ }
+
+ def getFromLdapInfo(
+ con: RwLDAPConnection,
+ nodeId: NodeId,
+ nodeEntry: LDAPEntry,
+ status: InventoryStatus,
+ needSoftware: Boolean
+ ): IOResult[Option[NodeFact]] = {
+ // mostly copied from com.normation.rudder.services.nodes.NodeInfoServiceCachedImpl # getBackendLdapNodeInfo
+ val ldapAttrs = (if (needSoftware) Seq(A_SOFTWARE_DN) else Seq()) ++ NodeInfoService.nodeInfoAttributes
+
+ con.get(inventoryDitService.getDit(status).NODES.NODE.dn(nodeId.value), ldapAttrs: _*).flatMap {
+ case None => // end of game, no node here
+ None.succeed
+ case Some(inv) =>
+ for {
+ optM <- inv(A_CONTAINER_DN) match {
+ case None => None.succeed
+ case Some(m) => con.get(new DN(m), ldapAttrs: _*)
+ }
+ info <- nodeMapper.convertEntriesToNodeInfos(nodeEntry, inv, optM)
+ soft <- getSoftware(con, fullInventoryRepository.getSoftwareUuids(inv), needSoftware)
+ } yield Some(NodeFact.fromCompat(info, Left(status), soft))
+ }
+ }
+
+ for {
+ t0 <- currentTimeMillis
+ _ <-
+ NodeLoggerPure.debug(
+ s"Getting node '${nodeId.value}' with inventory: ${SelectFacts
+ .retrieveInventory(attrs)}; software: ${attrs.software.mode == SelectMode.Retrieve}"
+ )
+ con <- ldap
+ optNode <- getNodeEntry(con, nodeId)
+ res <- optNode match {
+ case None => None.succeed
+ case Some(nodeEntry) =>
+ if (LdapNodeFactStorage.needsInventory(attrs)) {
+ getFromFullInventory(con, nodeId, nodeEntry, needsSoftware(attrs))
+ } else {
+ getFromLdapInfo(con, nodeId, nodeEntry, status, needsSoftware(attrs))
+ }
+ }
+ t1 <- currentTimeMillis
+ _ <- NodeLoggerPure.Metrics.debug(s"node '${nodeId.value}' retrieved in ${t1 - t0} ms")
+ _ <- NodeLoggerPure.Details.trace(s"node '${nodeId.value}' fetched details: ${res}")
+ } yield res
+ }
+
+ override def getPending(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ getNodeFact(nodeId, PendingInventory, attrs)
+ }
+
+ override def getAccepted(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ getNodeFact(nodeId, AcceptedInventory, attrs)
+ }
+
+ private[nodes] def getNodeIds(baseDN: DN): IOResult[Seq[NodeId]] = {
+ for {
+ con <- ldap
+ nodeEntries <- con.search(baseDN, One, ALL, "1.1")
+ } yield {
+ nodeEntries.flatMap(e => e(A_NODE_UUID).map(NodeId(_)))
+ }
+ }
+
+ private[nodes] def getAllNodeFacts(baseDN: DN, getOne: NodeId => IOResult[Option[NodeFact]]): IOStream[NodeFact] = {
+ ZStream
+ .fromZIO(getNodeIds(baseDN))
+ .tap(ids => NodeLoggerPure.Metrics.debug(s"Getting ${ids.size} nodes}"))
+ .flatMap(ids => ZStream.fromIterable(ids))
+ .mapZIO(getOne)
+ .flatMap(ZStream.fromIterable(_))
+ }
+
+ override def getAllPending()(implicit attrs: SelectFacts): IOStream[NodeFact] = {
+ getAllNodeFacts(inventoryDitService.getDit(PendingInventory).NODES.dn, getPending(_))
+ }
+
+ override def getAllAccepted()(implicit attrs: SelectFacts): IOStream[NodeFact] = {
+ getAllNodeFacts(nodeDit.NODES.dn, getAccepted)
+ }
+}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/inventory/PostCommits.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/inventory/PostCommits.scala
index 9621c7579fc..3aab8a799f6 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/inventory/PostCommits.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/inventory/PostCommits.scala
@@ -115,8 +115,8 @@ class PostCommitInventoryHooks[A](
}
class FactRepositoryPostCommit[A](
- nodeFactStorage: NodeFactStorage,
- nodeInfoService: NodeInfoService
+ nodeFactsRepository: NodeFactStorage,
+ nodeInfoService: NodeInfoService
) extends PostCommit[A] {
override def name: String = "commit node in fact-repository"
@@ -140,7 +140,7 @@ class FactRepositoryPostCommit[A](
ZIO.unit // does nothing
case Some(nodeInfo) =>
- nodeFactStorage.save(
+ nodeFactsRepository.save(
NodeFact.fromCompat(
nodeInfo,
Right(FullInventory(inventory.node, Some(inventory.machine))),
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/EventLogRepository.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/EventLogRepository.scala
index 6f56df1a47e..82d3c63b32c 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/EventLogRepository.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/EventLogRepository.scala
@@ -71,6 +71,7 @@ import com.normation.rudder.domain.workflows.ChangeRequestId
import com.normation.rudder.domain.workflows.WorkflowStepChange
import com.normation.rudder.services.eventlog.EventLogFactory
import doobie._
+import org.joda.time.DateTime
trait EventLogRepository {
def eventLogFactory: EventLogFactory
@@ -396,14 +397,16 @@ trait EventLogRepository {
modId: ModificationId,
principal: EventActor,
modifyDiff: ModifyNodeDiff,
- reason: Option[String]
+ reason: Option[String],
+ eventDate: DateTime
) = {
saveEventLog(
modId,
eventLogFactory.getModifyNodeFromDiff(
principal = principal,
modifyDiff = modifyDiff,
- reason = reason
+ reason = reason,
+ creationDate = eventDate
)
)
}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPEntityMapper.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPEntityMapper.scala
index 2ea1b8a91b6..2669e7140b9 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPEntityMapper.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPEntityMapper.scala
@@ -259,7 +259,9 @@ class LDAPEntityMapper(
)
.fail
}
- nodeInfo <- inventoryEntriesToNodeInfos(node, inventoryEntry, machineEntry)
+ nodeInfo <- inventoryEntriesToNodeInfos(node, inventoryEntry, machineEntry).chainError(
+ s"Error when getting '${node.id.value}' from LDAP"
+ )
} yield {
nodeInfo
}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPNodeRepository.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPNodeRepository.scala
index 1337daaeaed..81bf284f350 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPNodeRepository.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/repository/ldap/LDAPNodeRepository.scala
@@ -57,6 +57,7 @@ import com.normation.rudder.repository.WoNodeRepository
import com.normation.rudder.services.reports.CacheComplianceQueueAction
import com.normation.rudder.services.reports.CacheExpectedReportAction
import com.normation.rudder.services.reports.InvalidateCache
+import org.joda.time.DateTime
import zio._
import zio.syntax._
@@ -97,7 +98,7 @@ class WoLDAPNodeRepository(
case LDIFNoopChangeRecord(_) => ZIO.unit
case _ =>
val diff = ModifyNodeDiff(oldNode, node)
- actionLogger.saveModifyNode(modId, actor, diff, reason)
+ actionLogger.saveModifyNode(modId, actor, diff, reason, DateTime.now())
}
} yield {
node
@@ -164,7 +165,7 @@ class WoLDAPNodeRepository(
case _ =>
val diff =
ModifyNodeDiff.keyInfo(nodeId, agentsInfo._1.map(_.securityToken), agentsInfo._2, agentKey, agentKeyStatus)
- actionLogger.saveModifyNode(modId, actor, diff, reason)
+ actionLogger.saveModifyNode(modId, actor, diff, reason, DateTime.now())
}
} yield ())
}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/healthcheck/HealthcheckService.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/healthcheck/HealthcheckService.scala
index b7c3b7291c3..beb57f16bfa 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/healthcheck/HealthcheckService.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/healthcheck/HealthcheckService.scala
@@ -152,8 +152,8 @@ final class CheckFileDescriptorLimit(val nodeInfoService: NodeInfoService) exten
).fail
}
limit <- IOResult.attempt(res.stdout.trim.toLong)
+ nodeCount <- nodeInfoService.getNumberOfManagedNodes
} yield {
- val nodeCount = nodeInfoService.getNumberOfManagedNodes
val reasonableMaxLimit = 64_000
val approximatedMinLimit = 100 * nodeCount
// 64000 seems to be more than enough even with hundreds of nodes see https://issues.rudder.io/issues/22430
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/NewNodeManager.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/NewNodeManager.scala
index 05f91f7d29c..8f500e6ca4a 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/NewNodeManager.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/NewNodeManager.scala
@@ -37,36 +37,14 @@
package com.normation.rudder.services.servers
-import com.normation.box._
+import com.normation.errors.BoxToIO
+import com.normation.errors.Inconsistency
import com.normation.errors.IOResult
-import com.normation.eventlog.EventActor
-import com.normation.eventlog.ModificationId
import com.normation.inventory.domain.AcceptedInventory
-import com.normation.inventory.domain.FullInventory
-import com.normation.inventory.domain.InventoryStatus
import com.normation.inventory.domain.NodeId
-import com.normation.inventory.domain.PendingInventory
-import com.normation.inventory.domain.RemovedInventory
-import com.normation.inventory.ldap.core.InventoryDit
import com.normation.inventory.ldap.core.LDAPConstants._
-import com.normation.inventory.ldap.core.LDAPFullInventoryRepository
-import com.normation.inventory.services.core.FullInventoryRepository
-import com.normation.inventory.services.core.ReadOnlyFullInventoryRepository
-import com.normation.ldap.sdk.BuildFilter.ALL
-import com.normation.ldap.sdk.LDAPConnectionProvider
-import com.normation.ldap.sdk.RoLDAPConnection
-import com.normation.ldap.sdk.RwLDAPConnection
-import com.normation.rudder.batch.UpdateDynamicGroups
import com.normation.rudder.domain.Constants
-import com.normation.rudder.domain.NodeDit
-import com.normation.rudder.domain.eventlog.AcceptNodeEventLog
-import com.normation.rudder.domain.eventlog.InventoryLogDetails
-import com.normation.rudder.domain.eventlog.RefuseNodeEventLog
-import com.normation.rudder.domain.logger.NodeLogger
import com.normation.rudder.domain.logger.NodeLoggerPure
-import com.normation.rudder.domain.nodes.Node
-import com.normation.rudder.domain.nodes.NodeState
-import com.normation.rudder.domain.policies.PolicyMode
import com.normation.rudder.domain.queries.CriterionLine
import com.normation.rudder.domain.queries.DitQueryData
import com.normation.rudder.domain.queries.Equals
@@ -74,37 +52,20 @@ import com.normation.rudder.domain.queries.NodeReturnType
import com.normation.rudder.domain.queries.Or
import com.normation.rudder.domain.queries.Query
import com.normation.rudder.domain.queries.ResultTransformation
-import com.normation.rudder.domain.servers.Srv
-import com.normation.rudder.facts.nodes.NodeFact
-import com.normation.rudder.facts.nodes.NodeFactStorage
+import com.normation.rudder.facts.nodes.ChangeContext
+import com.normation.rudder.facts.nodes.CoreNodeFact
+import com.normation.rudder.facts.nodes.NodeFactRepository
+import com.normation.rudder.facts.nodes.SelectNodeStatus
import com.normation.rudder.hooks.HookEnvPairs
import com.normation.rudder.hooks.HooksLogger
import com.normation.rudder.hooks.RunHooks
-import com.normation.rudder.reports.ReportingConfiguration
-import com.normation.rudder.repository.CachedRepository
-import com.normation.rudder.repository.EventLogRepository
import com.normation.rudder.repository.RoNodeGroupRepository
import com.normation.rudder.repository.WoNodeGroupRepository
-import com.normation.rudder.repository.ldap.LDAPEntityMapper
-import com.normation.rudder.services.nodes.NodeInfoService
-import com.normation.rudder.services.nodes.history.HistoryLogRepository
-import com.normation.rudder.services.nodes.history.impl.FactLog
-import com.normation.rudder.services.nodes.history.impl.FactLogData
import com.normation.rudder.services.queries.QueryProcessor
-import com.normation.rudder.services.reports.CacheComplianceQueueAction
-import com.normation.rudder.services.reports.CacheComplianceQueueAction.ExpectedReportAction
-import com.normation.rudder.services.reports.CacheExpectedReportAction
-import com.normation.rudder.services.reports.CacheExpectedReportAction.InsertNodeInCache
-import com.normation.rudder.services.reports.InvalidateCache
-import com.normation.utils.Control.sequence
+import com.normation.zio._
import com.softwaremill.quicklens._
-import net.liftweb.common.Box
-import net.liftweb.common.Empty
-import net.liftweb.common.EmptyBox
-import net.liftweb.common.Failure
-import net.liftweb.common.Full
-import org.joda.time.DateTime
import zio.{System => _, _}
+import zio.stream.ZSink
import zio.syntax._
/**
@@ -113,7 +74,7 @@ import zio.syntax._
trait NewNodePostAcceptHooks {
def name: String
- def run(nodeId: NodeId): Unit
+ def run(nodeId: NodeId): IOResult[Unit]
}
@@ -124,9 +85,9 @@ trait NewNodeManagerHooks {
* their result. They are responsible to log
* their errors.
*/
- def afterNodeAcceptedAsync(nodeId: NodeId): Unit
+ def afterNodeAcceptedAsync(nodeId: NodeId): IOResult[Unit]
- def appendPostAcceptCodeHook(hook: NewNodePostAcceptHooks): Unit
+ def appendPostAcceptCodeHook(hook: NewNodePostAcceptHooks): IOResult[Unit]
}
/**
@@ -137,31 +98,31 @@ trait NewNodeManager {
/**
* List all pending node
*/
- def listNewNodes: Box[Seq[Srv]]
+ def listNewNodes: IOResult[Seq[CoreNodeFact]]
/**
* Accept a pending node in Rudder
*/
- def accept(id: NodeId, modId: ModificationId, actor: EventActor): Box[FullInventory]
+ def accept(id: NodeId)(implicit cc: ChangeContext): IOResult[CoreNodeFact]
/**
* refuse node
* @param ids : the node id
* @return : the srv representations of the refused node
*/
- def refuse(id: NodeId, modId: ModificationId, actor: EventActor): Box[Srv]
+ def refuse(id: NodeId)(implicit cc: ChangeContext): IOResult[CoreNodeFact]
/**
* Accept a list of pending nodes in Rudder
*/
- def accept(ids: Seq[NodeId], modId: ModificationId, actor: EventActor, actorIp: String): Box[Seq[FullInventory]]
+ def acceptAll(ids: Seq[NodeId])(implicit cc: ChangeContext): IOResult[Seq[CoreNodeFact]]
/**
* refuse a list of pending nodes
* @param ids : node ids
* @return : the srv representations of the refused nodes
*/
- def refuse(id: Seq[NodeId], modId: ModificationId, actor: EventActor, actorIp: String): Box[Seq[Srv]]
+ def refuseAll(ids: Seq[NodeId])(implicit cc: ChangeContext): IOResult[Seq[CoreNodeFact]]
}
@@ -173,38 +134,36 @@ trait NewNodeManager {
class PostNodeAcceptanceHookScripts(
HOOKS_D: String,
HOOKS_IGNORE_SUFFIXES: List[String],
- nodeInfoService: NodeInfoService
+ nodeFactRepository: NodeFactRepository
) extends NewNodePostAcceptHooks {
override def name: String = "new-node-post-accept-hooks"
- override def run(nodeId: NodeId): Unit = {
+ override def run(nodeId: NodeId): IOResult[Unit] = {
val systemEnv = {
import scala.jdk.CollectionConverters._
HookEnvPairs.build(System.getenv.asScala.toSeq: _*)
}
- val postHooksTime = System.currentTimeMillis
HooksLogger.debug(s"Executing post-node-acceptance hooks for node with id '${nodeId.value}'")
for {
- optNodeInfo <- nodeInfoService.getNodeInfo(nodeId).toBox
- nodeInfo <- optNodeInfo match {
- case None =>
- Failure(
+ postHooksTime0 <- currentTimeMillis
+ nodeFact <- nodeFactRepository
+ .get(nodeId)
+ .notOptional(
s"Just accepted node with id '${nodeId.value}' was not found - perhaps a bug?" +
" Please report with /var/log/rudder/webapp/DATE_OF_DAY.stdout.log file attached"
)
- case Some(x) => Full(x)
- }
- hookEnv = HookEnvPairs.build(
- ("RUDDER_NODE_ID", nodeInfo.id.value),
- ("RUDDER_NODE_HOSTNAME", nodeInfo.hostname),
- ("RUDDER_NODE_POLICY_SERVER_ID", nodeInfo.policyServerId.value),
- ("RUDDER_AGENT_TYPE", nodeInfo.agentsName.headOption.map(_.agentType.id).getOrElse(""))
- )
- postHooks <- RunHooks.getHooks(HOOKS_D + "/node-post-acceptance", HOOKS_IGNORE_SUFFIXES)
- runPostHook = RunHooks.syncRun(postHooks, hookEnv, systemEnv)
- timePostHooks = (System.currentTimeMillis - postHooksTime)
- _ = NodeLogger.PendingNode.debug(s"Node-post-acceptance scripts hooks ran in ${timePostHooks} ms")
+ hookEnv = HookEnvPairs.build(
+ ("RUDDER_NODE_ID", nodeFact.id.value),
+ ("RUDDER_NODE_HOSTNAME", nodeFact.fqdn),
+ ("RUDDER_NODE_POLICY_SERVER_ID", nodeFact.rudderSettings.policyServerId.value),
+ ("RUDDER_AGENT_TYPE", nodeFact.rudderAgent.agentType.id)
+ )
+ postHooks <- RunHooks.getHooksPure(HOOKS_D + "/node-post-acceptance", HOOKS_IGNORE_SUFFIXES)
+ runPostHook <- RunHooks.asyncRun(postHooks, hookEnv, systemEnv)
+ postHooksTime1 <- currentTimeMillis
+ timePostHooks = (postHooksTime1 - postHooksTime0)
+ _ <- NodeLoggerPure.PendingNode.debug(s"Node-post-acceptance scripts hooks ran in ${timePostHooks} ms")
} yield {
()
}
@@ -212,22 +171,26 @@ class PostNodeAcceptanceHookScripts(
}
class NewNodeManagerHooksImpl(
- nodeInfoService: NodeInfoService,
+ nodeFactRepository: NodeFactRepository,
HOOKS_D: String,
HOOKS_IGNORE_SUFFIXES: List[String]
) extends NewNodeManagerHooks {
- private[this] val codeHooks = collection.mutable.Buffer[NewNodePostAcceptHooks]()
-
- // by default, add the script hooks
- appendPostAcceptCodeHook(new PostNodeAcceptanceHookScripts(HOOKS_D, HOOKS_IGNORE_SUFFIXES, nodeInfoService))
+ val codeHooks = Ref
+ .make(
+ Chunk[NewNodePostAcceptHooks](
+ // by default, add the script hooks
+ new PostNodeAcceptanceHookScripts(HOOKS_D, HOOKS_IGNORE_SUFFIXES, nodeFactRepository)
+ )
+ )
+ .runNow
- override def afterNodeAcceptedAsync(nodeId: NodeId): Unit = {
- codeHooks.foreach(_.run(nodeId))
+ override def afterNodeAcceptedAsync(nodeId: NodeId): IOResult[Unit] = {
+ codeHooks.get.flatMap(hooks => ZIO.foreach(hooks)(h => IOResult.attempt(h.run(nodeId)))).unit
}
- def appendPostAcceptCodeHook(hook: NewNodePostAcceptHooks): Unit = {
- this.codeHooks.append(hook)
+ def appendPostAcceptCodeHook(hook: NewNodePostAcceptHooks): IOResult[Unit] = {
+ this.codeHooks.update(_.appended(hook))
}
}
@@ -244,600 +207,150 @@ class NewNodeManagerImpl[A](
listNodes: ListNewNode
) extends NewNodeManager {
- override def listNewNodes: Box[Seq[Srv]] = {
+ override def listNewNodes: IOResult[Seq[CoreNodeFact]] = {
listNodes.listNewNodes
}
- override def accept(id: NodeId, modId: ModificationId, actor: EventActor): Box[FullInventory] = {
- composedNewNodeManager.accept(id, modId, actor)
+ override def accept(id: NodeId)(implicit cc: ChangeContext): IOResult[CoreNodeFact] = {
+ composedNewNodeManager.accept(id)
}
- override def refuse(id: NodeId, modId: ModificationId, actor: EventActor): Box[Srv] = {
- composedNewNodeManager.refuse(id, modId, actor)
+ override def refuse(id: NodeId)(implicit cc: ChangeContext): IOResult[CoreNodeFact] = {
+ composedNewNodeManager.refuse(id)
}
- override def accept(ids: Seq[NodeId], modId: ModificationId, actor: EventActor, actorIp: String): Box[Seq[FullInventory]] = {
- composedNewNodeManager.accept(ids, modId, actor, actorIp)
+ override def acceptAll(ids: Seq[NodeId])(implicit cc: ChangeContext): IOResult[Seq[CoreNodeFact]] = {
+ composedNewNodeManager.acceptAll(ids)
}
- override def refuse(id: Seq[NodeId], modId: ModificationId, actor: EventActor, actorIp: String): Box[Seq[Srv]] = {
- composedNewNodeManager.refuse(id, modId, actor, actorIp)
+ override def refuseAll(id: Seq[NodeId])(implicit cc: ChangeContext): IOResult[Seq[CoreNodeFact]] = {
+ composedNewNodeManager.refuseAll(id)
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
trait ListNewNode {
- def listNewNodes: Box[Seq[Srv]]
+ def listNewNodes: IOResult[Seq[CoreNodeFact]]
}
-class LdapListNewNode(
- ldap: LDAPConnectionProvider[RoLDAPConnection],
- serverSummaryService: NodeSummaryServiceImpl,
- pendingNodesDit: InventoryDit
-) extends ListNewNode {
- override def listNewNodes: Box[Seq[Srv]] = {
- for {
- con <- ldap
- seq <- con.searchOne(pendingNodesDit.NODES.dn, ALL, Srv.ldapAttributes: _*)
- srvs <- ZIO.foreach(seq) { e =>
- serverSummaryService
- .makeSrv(e)
- .foldZIO(
- err =>
- IOResult.attempt(
- NodeLogger.PendingNode
- .debug(s"Error when mapping a pending node entry '${e.dn}' to a node object. Error was: ${err.fullMsg}")
- ) *>
- None.succeed,
- srv => Some(srv).succeed
- )
- }
- } yield {
- srvs.flatten
- }
- }.toBox
+class FactListNewNodes(backend: NodeFactRepository) extends ListNewNode {
+ override def listNewNodes: IOResult[Seq[CoreNodeFact]] = {
+ backend.getAll()(SelectNodeStatus.Pending).run(ZSink.collectAll)
+ }
}
trait UnitRefuseInventory {
def name: String
- def refuseOne(srv: Srv, modId: ModificationId, actor: EventActor): Box[Srv]
+ def refuseOne(cnf: CoreNodeFact)(implicit cc: ChangeContext): IOResult[Unit]
}
-trait UnitAcceptInventory {
+/*
+ * Checks running before actually accepting the inventory.
+ * If one is in error, acceptation of node fails.
+ */
+trait UnitCheckAcceptInventory {
/**
* A name to describe the role of that acceptor
*/
def name: String
- /**
- * The status of the inventory before that action
- */
- def fromInventoryStatus: InventoryStatus
-
- /**
- * The status of the inventory after that action
- */
- def toInventoryStatus: InventoryStatus
-
- /**
- * What to do ?
- */
- def acceptOne(sm: FullInventory, modId: ModificationId, actor: EventActor): Box[FullInventory]
-
/**
* An action to execute before the whole batch
*/
- def preAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]]
-
- /**
- * An action to execute after the whole batch
- */
- def postAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]]
-
- /**
- * Execute a rollback for the given inventory
- */
- def rollback(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Unit
-
- def rollbackErrorMsg(e: EmptyBox, id: String): String = {
- val msg = "Error when rollbacking server node id %s in process '%s', you should delete it by hand. ".format(id, this.name)
-
- e match {
- case Empty => msg + "No error message was left."
- case f: Failure => msg + "Error messages: " + f.messageChain
- }
- }
-
+ def preAccept(cnf: CoreNodeFact)(implicit cc: ChangeContext): IOResult[Unit]
}
class ComposedNewNodeManager[A](
- smRepo: FullInventoryRepository[A],
- serverSummaryService: NodeSummaryService,
- unitAcceptors: Seq[UnitAcceptInventory],
- unitRefusors: Seq[UnitRefuseInventory],
- historyLogRepository: HistoryLogRepository[NodeId, DateTime, FactLogData, FactLog],
- eventLogRepository: EventLogRepository,
- updateDynamicGroups: UpdateDynamicGroups,
- cachedNodeConfigurationService: InvalidateCache[CacheExpectedReportAction],
- cachedReportingService: InvalidateCache[CacheComplianceQueueAction],
- cacheToClear: List[CachedRepository],
- hooksRunner: NewNodeManagerHooks
+ nodeFactRepo: NodeFactRepository,
+ unitAcceptors: Seq[UnitCheckAcceptInventory],
+ unitRefusors: Seq[UnitRefuseInventory],
+ hooksRunner: NewNodeManagerHooks
) {
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// Refuse //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
- /**
- * Retrieve the last inventory for the selected server
- */
- def retrieveLastVersions(nodeId: NodeId): Option[DateTime] = {
- historyLogRepository.versions(nodeId).toBox.flatMap(_.headOption)
- }
-
/**
* Refuse one server
*/
- private[this] def refuseOne(srv: Srv, modId: ModificationId, actor: EventActor): Box[Srv] = {
- var errors = Option.empty[Failure]
- unitRefusors.foreach { refusor =>
- try {
- refusor.refuseOne(srv, modId, actor) match {
- case e: EmptyBox =>
- val msg = "Error refusing %s: step %s".format(srv.id, refusor.name)
- errors match {
- case None => errors = Some(e ?~! msg)
- case Some(old) => errors = Some(Failure(msg, Empty, Full(old)))
- }
- case Full(x) =>
- NodeLogger.PendingNode.trace("Refuse %s: step %s ok".format(srv.id, refusor.name))
- }
- } catch {
- case e: Exception =>
- val msg = "Error when trying to executre the step %s, when refusing inventory %".format(refusor.name, srv.id)
- errors match {
- case None => errors = Some(Failure(msg, Full(e), Empty))
- case Some(old) => errors = Some(Failure(msg, Full(e), Full(old)))
- }
- }
- }
- errors match {
- case Some(f) => f
- case None => Full(srv)
- }
+ private[this] def refuseOne(cnf: CoreNodeFact)(implicit cc: ChangeContext): IOResult[Unit] = {
+ ZIO
+ .foreach(unitRefusors)(r => {
+ r.refuseOne(cnf)
+ .tapBoth(
+ err => NodeLoggerPure.PendingNode.error(s"Error when refusing node '${cnf.id.value}': ${err.fullMsg}"),
+ _ => NodeLoggerPure.PendingNode.debug(s"Refusing node '${cnf.id.value}' step '${r.name}': OK")
+ )
+ })
+ .unit
}
- def refuse(id: NodeId, modId: ModificationId, actor: EventActor): Box[Srv] = {
+ def refuse(id: NodeId)(implicit cc: ChangeContext): IOResult[CoreNodeFact] = {
for {
- srvs <- serverSummaryService.find(PendingInventory, id)
- srv <-
- if (srvs.size == 1) Full(srvs(0)) else Failure("Found several pending nodes matching id %s: %s".format(id.value, srvs))
- refuse <- refuseOne(srv, modId, actor)
- } yield {
- refuse
- }
+ cnf <-
+ nodeFactRepo.get(id)(SelectNodeStatus.Pending).notOptional(s"Node with id '${id.value}' was not found in pending nodes")
+ _ <- refuseOne(cnf)
+ _ <- nodeFactRepo.delete(id)
+ } yield cnf
}
- def refuse(ids: Seq[NodeId], modId: ModificationId, actor: EventActor, actorIp: String): Box[Seq[Srv]] = {
-
- // Best effort it, starting with an empty result
- val start: Box[Seq[Srv]] = Full(Seq())
- ids.foldLeft(start) {
- case (result, id) =>
- // Refuse the node and get the result
- val refusal = for {
- srvs <- serverSummaryService.find(PendingInventory, id)
- // I don't think this is possible, either we have one, either we don't have any
- srv <- if (srvs.size == 1) {
- Full(srvs.head)
- } else {
- Failure(s"Found ${srvs.size} pending nodes matching id ${id.value}: ${srvs.mkString(", ")}")
- }
- refuse <- refuseOne(srv, modId, actor)
- } yield {
-
- // Make an event log of the refusale
- retrieveLastVersions(srv.id) match {
- case Some(x) =>
- val inventoryDetails = InventoryLogDetails(
- nodeId = srv.id,
- inventoryVersion = x,
- hostname = srv.hostname,
- fullOsName = srv.osFullName,
- actorIp = actorIp
- )
- val entry = RefuseNodeEventLog.fromInventoryLogDetails(
- principal = actor,
- inventoryDetails = inventoryDetails
- )
-
- eventLogRepository.saveEventLog(modId, entry).toBox match {
- case Full(_) =>
- NodeLogger.PendingNode.debug(s"Successfully refused node '${id.value}'")
- case _ =>
- NodeLogger.PendingNode.warn(s"Node '${id.value}' refused, but the action couldn't be logged")
- }
- case None =>
- NodeLogger.PendingNode.warn(s"Node '${id}' refused, but couldn't find it's inventory")
- }
- refuse
- }
-
- // accumulate result
- (refusal, result) match {
- // Node deleted, and result ok, accumulate success
- case (Full(srv), Full(seq)) =>
- Full(srv +: seq)
- // Node deleted, but there was an error before, keep error
- case (Full(_), error) =>
- error
- // An error while deleting, and there was none, create a new error
- case (eb: EmptyBox, Full(_)) =>
- eb
- // A new error while deleting, and there was already one, accumulate error
- case (eb: EmptyBox, result: EmptyBox) =>
- // That message will not be used, since we have 'Failure' and no 'Empty'
- val error = eb ?~ "An error occured while refusing a Node"
- result ?~! error.messageChain
- }
- }
+ def refuseAll(ids: Seq[NodeId])(implicit cc: ChangeContext): IOResult[Seq[CoreNodeFact]] = {
+ ZIO.foreachPar(ids)(id => refuse(id))
}
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// Accept //////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
- private[this] def rollback(
- unitAcceptors: Seq[UnitAcceptInventory],
- rollbackOn: Seq[FullInventory],
- modId: ModificationId,
- actor: EventActor
- ): Unit = {
- NodeLogger.PendingNode.debug(
- "\n*****************************************************\nRollbacking\n*****************************************************"
- )
-
- for {
- toRollback <- unitAcceptors.reverse
- } {
- NodeLogger.PendingNode.debug(
- "Rollbacking %s for %s".format(toRollback.name, rollbackOn.map(_.node.main.id.value).mkString("; "))
- )
- try {
- toRollback.rollback(rollbackOn, modId, actor)
- } catch {
- case e: Exception => NodeLogger.PendingNode.error("Error when rollbacking acceptor process '%s'".format(toRollback.name))
- }
- }
- }
-
- def accept(id: NodeId, modId: ModificationId, actor: EventActor): Box[FullInventory] = {
- accept(List(id), modId, actor, "rudder-ui").flatMap {
- case h +: _ => Full(h)
- case _ =>
- Failure(
- s"Error when trying to accept node with ID: '${id.value}'. The acceptation method returned " +
- s"an empty result, it is likely a bug, please report it."
- )
- }
- }
-
- def accept(ids: Seq[NodeId], modId: ModificationId, actor: EventActor, actorIp: String): Box[Seq[FullInventory]] = {
-
- // Get inventory from a nodeId
- def getInventory(nodeId: NodeId) = {
- smRepo.get(nodeId, PendingInventory).toBox match {
- case Full(x) => Full(x)
- case eb: EmptyBox =>
- val msg = s"Can not accept not found inventory with id '${nodeId.value}'"
- eb ?~! msg
- }
- }
+ def accept(id: NodeId)(implicit cc: ChangeContext): IOResult[CoreNodeFact] = {
// validate pre acceptance for a Node, if an error occurs, stop everything on that node.
- def passPreAccept(inventory: FullInventory) = {
- sequence(unitAcceptors)(unitAcceptor => {
- unitAcceptor.preAccept(Seq(inventory), modId, actor) match {
- case Full(seq) => // ok, cool
- NodeLogger.PendingNode.debug(s"Pre acceptance phase: '${unitAcceptor.name}' OK")
- Full(seq)
- case eb: EmptyBox => // on an error here, stop
- val id = inventory.node.main.id.value
- val msg = s"Error when trying to add node with id '${id}'"
- val e = eb ?~! msg
- NodeLogger.PendingNode.error(e.messageChain)
- NodeLogger.PendingNode.debug(
- s"Node with id '${id}' was refused during 'pre-accepting' step of phase '${unitAcceptor.name}'"
- )
- e
- }
- })
- }
-
- // accept one node
- def acceptOne(sm: FullInventory, modId: ModificationId, actor: EventActor): Box[FullInventory] = {
- (sequence(unitAcceptors) { unitAcceptor =>
- try {
- unitAcceptor.acceptOne(sm, modId, actor) ?~! "Error when executing accept node process named %s".format(
- unitAcceptor.name
- )
- } catch {
- case e: Exception => {
- NodeLogger.PendingNode.debug("Exception in unit acceptor %s".format(unitAcceptor.name), e)
- Failure(e.getMessage, Full(e), Empty)
- }
- }
- }) match {
- case Full(seq) => Full(sm)
- case e: EmptyBox => // rollback that one
- NodeLogger.PendingNode.error(
- (e ?~! "Error when trying to accept node %s. Rollbacking.".format(sm.node.main.id.value)).messageChain
+ def passPreAccept(nodeFact: CoreNodeFact) = {
+ ZIO.foreachDiscard(unitAcceptors) { a =>
+ a.preAccept(nodeFact)
+ .tapBoth(
+ err =>
+ NodeLoggerPure.PendingNode.debug(
+ s"Node with id '${nodeFact.id.value}' was refused during 'pre-accepting' step of phase '${a.name}'"
+ ) *>
+ NodeLoggerPure.PendingNode
+ .error(s"Error when trying to accept node with id '${nodeFact.id.value}': ${err.fullMsg}"),
+ _ => NodeLoggerPure.PendingNode.debug(s"Pre acceptance phase: '${a.name}' OK for node ${nodeFact.id.value}")
)
- rollback(unitAcceptors, Seq(sm), modId, actor)
- e
}
}
- // validate post acceptance for a Node, if an error occurs, Rollback the node acceptance
- def passPostAccept(inventory: FullInventory) = {
- sequence(unitAcceptors)(unitAcceptor => {
- unitAcceptor.postAccept(Seq(inventory), modId, actor) match {
- case Full(seq) => // ok, cool
- NodeLogger.PendingNode.debug(s"Post acceptance phase: '${unitAcceptor.name}' OK")
- Full(seq)
- case eb: EmptyBox => // on an error here, rollback
- val msg = s"Error when trying to execute post-accepting for phase '${unitAcceptor.name}. Rollback."
- val e = eb ?~! msg
- NodeLogger.PendingNode.error(e.messageChain)
- rollback(unitAcceptors, Seq(inventory), modId, actor)
- e
- }
- })
- }
-
- // Get all acceptance, we will not accumulate errors in that pass, so we can start a promise generation
- val acceptanceResults = ids.map(id => {
- for {
- // Get inventory og the node
- inventory <- getInventory(id).flatMap {
- case None => Failure(s"Missing inventory for node with ID: '${id.value}'")
- case Some(i) => Full(i)
- }
- // Pre accept it
- preAccept <- passPreAccept(inventory)
- // Accept it
- acceptationResults <- acceptOne(inventory, modId, actor) ?~! s"Error when trying to accept node ${id.value}"
- _ = NodeLogger.PendingNode.debug(s"Unit acceptors ok for '${id.value}'")
- // Post accept it
- postAccept <- passPostAccept(inventory)
- } yield {
-
- // Make an event log for acceptance
- retrieveLastVersions(id) match {
- case Some(x) =>
- val inventoryDetails = InventoryLogDetails(
- nodeId = id,
- inventoryVersion = x,
- hostname = inventory.node.main.hostname,
- fullOsName = inventory.node.main.osDetails.fullName,
- actorIp = actorIp
- )
- val entry = AcceptNodeEventLog.fromInventoryLogDetails(
- principal = actor,
- inventoryDetails = inventoryDetails
- )
-
- eventLogRepository.saveEventLog(modId, entry).toBox match {
- case Full(_) =>
- NodeLogger.info(s"New node accepted and managed by Rudder: ${id.value}")
- case _ =>
- NodeLogger.PendingNode.warn(s"Node '${id.value}' accepted, but the action couldn't be logged")
- }
-
- case None =>
- NodeLogger.PendingNode.warn(s"Node '${id.value}' accepted, but couldn't find it's inventory")
- }
-
- // Update hooks for the node
- hooksRunner.afterNodeAcceptedAsync(id)
- // ping the NodeConfiguration Cache and NodeCompliance Cache about this new node
-
- for {
- _ <- cachedNodeConfigurationService
- .invalidateWithAction(Seq((id, InsertNodeInCache(id))))
- .toBox ?~! s"Error when adding node ${id.value} to node configuration cache"
- _ <- cachedReportingService
- .invalidateWithAction(Seq((id, ExpectedReportAction(InsertNodeInCache(id)))))
- .toBox ?~! s"Error when adding node ${id.value} to compliance cache"
- _ <- ZIO.foreach(cacheToClear)(c => IOResult.attempt(c.clearCache())).toBox
- } yield {
- ()
- }
- acceptationResults
- }
- })
-
- // If one node succeed, then update policies
- if (acceptanceResults.exists { case Full(_) => true; case _ => false }) {
- updateDynamicGroups.startManualUpdate
- }
-
- // Transform the sequence of box into a boxed result, best effort it!
- acceptanceResults.foldRight(Full(Seq()): Box[Seq[FullInventory]]) {
- // Node accepted, and result ok, accumulate success
- case (Full(inv), Full(seq)) =>
- Full(inv +: seq)
- // Node accepted, but there was an error before, keep error
- case (Full(_), error) =>
- error
- // An error while accepting, and there was none, create a new error
- case (eb: EmptyBox, Full(_)) =>
- eb
- // A new error while accepting, and there was already one, accumulate error
- case (eb: EmptyBox, result: EmptyBox) =>
- // That message will not be used, since we have 'Failure' and no 'Empty'
- val error = eb ?~ "An error occured while refusing a Node"
- result ?~! error.messageChain
- }
- }
-
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Unit acceptor for the inventory part.
- * Essentially: move FullInventory information
- * where they belongs.
- *
- */
-class AcceptInventory(
- override val name: String,
- pendingNodesDit: InventoryDit,
- acceptedNodesDit: InventoryDit,
- smRepo: LDAPFullInventoryRepository
-) extends UnitAcceptInventory with UnitRefuseInventory {
-
- override def preAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(
- sms
- ) // nothing to do
-
- override def postAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(
- sms
- ) // nothing to do
-
- override val fromInventoryStatus = PendingInventory
-
- override val toInventoryStatus = AcceptedInventory
-
- def acceptOne(sm: FullInventory, modId: ModificationId, actor: EventActor): Box[FullInventory] = {
-
- smRepo.move(sm.node.main.id, fromInventoryStatus, toInventoryStatus).toBox.map(_ => sm)
- }
-
- def rollback(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Unit = {
- sms.foreach { sm =>
- // rollback from accepted
- (for {
- result <- smRepo.move(sm.node.main.id, toInventoryStatus, fromInventoryStatus).toBox
- } yield {
- result
- }) match {
- case e: EmptyBox => NodeLogger.PendingNode.error(rollbackErrorMsg(e, sm.node.main.id.value))
- case Full(f) =>
- NodeLogger.PendingNode.debug("Succesfully rollbacked %s for process '%s'".format(sm.node.main.id, this.name))
- }
- }
+ for {
+ // Get inventory og the node
+ cnf <- nodeFactRepo.get(id)(SelectNodeStatus.Pending).notOptional(s"Missing inventory for node with ID: '${id.value}'")
+ // Pre accept it
+ preAccept <- passPreAccept(cnf)
+ // Accept it
+ _ <- nodeFactRepo.changeStatus(id, AcceptedInventory)
+ // Update hooks for the node
+ _ <- hooksRunner
+ .afterNodeAcceptedAsync(id)
+ .catchAll(err => {
+ NodeLoggerPure.PendingNode.error(
+ s"Error when executing post-acceptation hooks for node '${cnf.fqdn}' " +
+ s"[${cnf.id.value}]: ${err.fullMsg}"
+ )
+ })
+ } yield cnf.modify(_.rudderSettings.status).setTo(AcceptedInventory)
}
- //////////// refuse ////////////
- override def refuseOne(srv: Srv, modId: ModificationId, actor: EventActor): Box[Srv] = {
- // refuse an inventory: delete it
- smRepo.delete(srv.id, fromInventoryStatus).toBox.map(_ => srv)
+ def acceptAll(ids: Seq[NodeId])(implicit cc: ChangeContext): IOResult[Seq[CoreNodeFact]] = {
+ ZIO.foreachPar(ids)(id => accept(id))
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Accept FullInventory at ou=node level: just add it
- */
-class AcceptFullInventoryInNodeOu(
- override val name: String,
- nodeDit: NodeDit,
- ldap: LDAPConnectionProvider[RwLDAPConnection],
- ldapEntityMapper: LDAPEntityMapper,
- inventoryStatus: InventoryStatus, // expected inventory status of nodes for that processor
-
- defaultPolicyMode: () => Box[Option[PolicyMode]],
- defaultNodeState: () => Box[NodeState]
-) extends UnitAcceptInventory with UnitRefuseInventory {
-
- override def preAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(
- sms
- ) // nothing to do
-
- override def postAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(
- sms
- ) // nothing to do
-
- override val fromInventoryStatus = inventoryStatus
-
- override val toInventoryStatus = inventoryStatus
-
- /**
- * Add a node entry in ou=Nodes
- */
- def acceptOne(sm: FullInventory, modId: ModificationId, actor: EventActor): Box[FullInventory] = {
- val name = sm.node.name.getOrElse(sm.node.main.id.value)
- val description = sm.node.description.getOrElse("")
-
- // naive test to find if the node is the master policy server.
- // TODO: that can not handle relay server
- val isPolicyServer = sm.node.main.id == sm.node.main.policyServerId
-
- val node = Node(
- sm.node.main.id,
- name,
- description,
- defaultNodeState().openOr(NodeState.Enabled),
- false,
- isPolicyServer,
- DateTime.now, // won't be used on save - dummy value
-
- ReportingConfiguration(None, None, None), // use global schedule, and default configuration for reporting
-
- Nil, // no user properties for now
-
- defaultPolicyMode().openOr(None)
- )
-
- val entry = ldapEntityMapper.nodeToEntry(node)
- for {
- con <- ldap
- res <- con.save(entry).chainError(s"Error when trying to save node '${entry.dn}' in process '${this.name}'")
- } yield {
- sm
- }
- }.toBox
-
- /**
- * Just remove the node entry
- */
- def rollback(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Unit = {
- sms.foreach { sm =>
- (for {
- con <- ldap
- dn = nodeDit.NODES.NODE.dn(sm.node.main.id.value)
- result <- con.delete(dn)
- } yield {
- result
- }).toBox match {
- case e: EmptyBox => NodeLogger.PendingNode.error(rollbackErrorMsg(e, sm.node.main.id.value))
- case Full(f) =>
- NodeLogger.PendingNode.debug("Succesfully rollbacked %s for process '%s'".format(sm.node.main.id, this.name))
- }
- }
- }
-
- //////////// refuse ////////////
- override def refuseOne(srv: Srv, modId: ModificationId, actor: EventActor): Box[Srv] = {
- // refuse ou=nodes: delete it
- for {
- con <- ldap
- dn = nodeDit.NODES.NODE.dn(srv.id.value)
- result <- con.delete(dn)
- } yield {
- srv
- }
- }.toBox
-
-}
-
class RefuseGroups(
override val name: String,
roGroupRepo: RoNodeGroupRepository,
@@ -845,20 +358,20 @@ class RefuseGroups(
) extends UnitRefuseInventory {
//////////// refuse ////////////
- override def refuseOne(srv: Srv, modId: ModificationId, actor: EventActor): Box[Srv] = {
+ override def refuseOne(cnf: CoreNodeFact)(implicit cc: ChangeContext): IOResult[Unit] = {
// remove server id in all groups
for {
- groupIds <- roGroupRepo.findGroupWithAnyMember(Seq(srv.id))
+ groupIds <- roGroupRepo.findGroupWithAnyMember(Seq(cnf.id))
modifiedGroups <- ZIO.foreach(groupIds) { groupId =>
for {
groupPair <- roGroupRepo.getNodeGroup(groupId)
- modGroup = groupPair._1.copy(serverList = groupPair._1.serverList - srv.id)
- msg = Some("Automatic update of groups due to refusal of node " + srv.id.value)
+ modGroup = groupPair._1.copy(serverList = groupPair._1.serverList - cnf.id)
+ msg = Some("Automatic update of groups due to refusal of node " + cnf.id.value)
saved <- {
val res = if (modGroup.isSystem) {
- woGroupRepo.updateSystemGroup(modGroup, modId, actor, msg)
+ woGroupRepo.updateSystemGroup(modGroup, cc.modId, cc.actor, cc.message)
} else {
- woGroupRepo.update(modGroup, modId, actor, msg)
+ woGroupRepo.update(modGroup, cc.modId, cc.actor, cc.message)
}
res
}
@@ -866,10 +379,8 @@ class RefuseGroups(
saved
}
}
- } yield {
- srv
- }
- }.toBox
+ } yield ()
+ }
}
/**
@@ -879,19 +390,23 @@ class RefuseGroups(
*/
class AcceptHostnameAndIp(
override val name: String,
- inventoryStatus: InventoryStatus,
queryProcessor: QueryProcessor,
ditQueryData: DitQueryData,
policyServerNet: PolicyServerManagementService,
- nodeInfoService: NodeInfoService,
+ nodeFactRepo: NodeFactRepository,
acceptDuplicateHostnames: IOResult[Boolean]
-) extends UnitAcceptInventory {
+) extends UnitCheckAcceptInventory {
// return the list of ducplicated hostname from user input - we want that to be empty
- private[this] def checkDuplicateString(attributes: Seq[String], attributeName: String): Box[Unit] = {
+ private[this] def checkDuplicateString(attributes: Seq[String], attributeName: String): IOResult[Unit] = {
val duplicates = attributes.groupBy(x => x).collect { case (k, v) if v.size > 1 => v.head }.toSeq.sorted
- if (duplicates.isEmpty) Full({})
- else Failure("You can not accept two nodes with the same %s: %s".format(attributeName, duplicates.mkString("'", "', ", "'")))
+ ZIO
+ .when(duplicates.nonEmpty) {
+ Inconsistency(
+ s"You can not accept two nodes with the same ${attributeName}: ${duplicates.mkString("'", "', ", "'")}"
+ ).fail
+ }
+ .unit
}
// some constant data for the query about hostname on node
@@ -908,14 +423,11 @@ class AcceptHostnameAndIp(
* search in database nodes having the same hostname as one provided.
* Only return existing hostname (and so again, we want that to be empty)
*/
- private[this] def queryForDuplicateHostname(hostnames: Seq[String]): Box[Unit] = {
+ private[this] def queryForDuplicateHostname(hostnames: Seq[String]): IOResult[Unit] = {
def failure(duplicates: Seq[String], name: String) = {
- Failure(
- "There is already a node with %s %s in database. You can not add it again.".format(
- name,
- duplicates.mkString("'", "' or '", "'")
- )
- )
+ Inconsistency(
+ s"There is already a node with ${name} ${duplicates.mkString("'", "' or '", "'")} in database. You can not add it again."
+ ).fail
}
val hostnameCriterion = hostnames.toList.map { h =>
@@ -928,184 +440,36 @@ class AcceptHostnameAndIp(
}
for {
- duplicatesH <- queryProcessor.process(Query(NodeReturnType, Or, ResultTransformation.Identity, hostnameCriterion))
+ duplicatesH <- BoxToIO(
+ queryProcessor.process(Query(NodeReturnType, Or, ResultTransformation.Identity, hostnameCriterion))
+ ).toIO
// here, all nodes found are duplicate-in-being. They should be unique, but
// if not, we don't group them that the duplicate appears in the list
- noDuplicatesH <- if (duplicatesH.isEmpty) Full({})
+ noDuplicatesH <- if (duplicatesH.isEmpty) ZIO.unit
else {
// get the hostname from nodeInfoService
- for {
- nodesInfo <- nodeInfoService.getNodeInfosSeq(duplicatesH).toBox
- hostnames = nodesInfo.map(ni => ni.hostname)
- } yield {
- failure(hostnames, "Hostname")
- }
+ nodeFactRepo
+ .getAll()
+ .collect { case n if (duplicatesH.contains(n.id)) => n.fqdn }
+ .runCollect
+ .flatMap(failure(_, "Hostname"))
}
- } yield {}
+ } yield ()
}
- override def preAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = {
-
- val hostnames = sms.map(_.node.main.hostname)
-
+ override def preAccept(cnf: CoreNodeFact)(implicit cc: ChangeContext): IOResult[Unit] = {
for {
authorizedNetworks <-
policyServerNet
.getAllowedNetworks(Constants.ROOT_POLICY_SERVER_ID)
- .toBox ?~! "Can not get authorized networks: check their configuration, and that rudder-init was done"
- acceptDuplicated <- acceptDuplicateHostnames.toBox
- _ <- if (acceptDuplicated) Full(())
- else {
+ .chainError("Can not get authorized networks: check their configuration, and that rudder-init was done")
+ acceptDuplicated <- acceptDuplicateHostnames
+ _ <- ZIO.when(!acceptDuplicated) {
for {
- noDuplicateHostnames <- checkDuplicateString(hostnames, "hostname")
- noDuplicateInDB <- queryForDuplicateHostname(hostnames)
+ noDuplicateHostnames <- checkDuplicateString(List(cnf.fqdn), "hostname")
+ noDuplicateInDB <- queryForDuplicateHostname(List(cnf.fqdn))
} yield ()
}
- } yield {
- sms
- }
- }
-
- override val fromInventoryStatus = inventoryStatus
-
- override val toInventoryStatus = inventoryStatus
-
- /**
- * Only add the server to the list of children of the policy server
- */
- def acceptOne(sm: FullInventory, modId: ModificationId, actor: EventActor): Box[FullInventory] = Full(sm)
-
- /**
- * An action to execute after the whole batch
- */
- def postAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(sms)
-
- /**
- * Execute a rollback for the given inventory
- */
- def rollback(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Unit = {}
-}
-
-/**
- * A unit acceptor in charge to historize the
- * state of the Inventory so that we can keep it
- * forever.
- * That acceptor should be call before node
- * is actually deleted or accepted
- */
-class HistorizeNodeStateOnChoice(
- override val name: String,
- repos: ReadOnlyFullInventoryRepository,
- historyRepos: HistoryLogRepository[NodeId, DateTime, FactLogData, FactLog],
- inventoryStatus: InventoryStatus // expected inventory status of nodes for that processor
-) extends UnitAcceptInventory with UnitRefuseInventory {
-
- override def preAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(
- sms
- ) // nothing to do
-
- override def postAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(
- sms
- ) // nothing to do
-
- override val fromInventoryStatus = inventoryStatus
-
- override val toInventoryStatus = inventoryStatus
-
- /**
- * Add a node entry in ou=Nodes
- */
- def acceptOne(sm: FullInventory, modId: ModificationId, actor: EventActor): Box[FullInventory] = {
- // set status to "acccepted" before historisation
- val postSM = sm.modify(_.node.main.status).setTo(AcceptedInventory)
- historyRepos
- .save(postSM.node.main.id, FactLogData(NodeFact.newFromFullInventory(postSM, None), actor, AcceptedInventory))
- .toBox
- .map(_ => postSM)
- }
-
- /**
- * Does nothing - we don't have the "id" of the historized
- * inventory to remove
- */
- def rollback(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Unit = {}
-
- //////////// refuse ////////////
- override def refuseOne(srv: Srv, modId: ModificationId, actor: EventActor): Box[Srv] = {
- // refuse ou=nodes: delete it
- for {
- full <- repos.get(srv.id, inventoryStatus)
- _ <- full match {
- case None =>
- "ok".succeed
- case Some(inv) =>
- historyRepos.save(
- srv.id,
- FactLogData(
- NodeFact.newFromFullInventory(inv.modify(_.node.main.status).setTo(RemovedInventory), None),
- actor,
- RemovedInventory
- )
- )
- }
- } yield srv
- }.toBox
-}
-
-/**
- * A unit acceptor in charge do what is necessary regarding fact repo.
- * In the long term, it will replace `HistorizeNodeStateOnChoice`.
- * Semantic:
- * - if a node is refused, delete+commit its inventory
- * - if a node is accepted, move to accepted subdif+commit its inventory
- */
-class UpdateFactRepoOnChoice(
- override val name: String,
- inventoryStatus: InventoryStatus, // expected inventory status of nodes for that processor
- nodeFactStorage: NodeFactStorage
-) extends UnitAcceptInventory with UnitRefuseInventory {
- override def preAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(
- sms
- ) // nothing to do
- override def postAccept(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Box[Seq[FullInventory]] = Full(
- sms
- ) // nothing to do
- override val fromInventoryStatus = inventoryStatus
- override val toInventoryStatus = inventoryStatus
-
- /**
- * Move node fact from fact-repo/pending to fact-repo/accepted
- */
- def acceptOne(sm: FullInventory, modId: ModificationId, actor: EventActor): Box[FullInventory] = {
- // in 7.0, we don't fail on historization problem, only log
- nodeFactStorage
- .changeStatus(sm.node.main.id, AcceptedInventory)
- .catchAll(err => {
- NodeLoggerPure.info(
- s"Error when trying to update facts historization when accepting node '${sm.node.main.hostname}' (${sm.node.main.id.value})"
- )
- })
- .toBox
- .map(_ => sm)
- }
-
- /**
- * Does nothing - we don't have the "id" of the historized
- * inventory to remove
- */
- def rollback(sms: Seq[FullInventory], modId: ModificationId, actor: EventActor): Unit = {}
-
- //////////// refuse ////////////
- override def refuseOne(srv: Srv, modId: ModificationId, actor: EventActor): Box[Srv] = {
- // in 7.0, we don't fail on historization problem, only log
- nodeFactStorage
- .changeStatus(srv.id, RemovedInventory)
- .catchAll(err => {
- NodeLoggerPure.info(
- s"Error when trying to update facts historization when accepting node '${srv.hostname}' (${srv.id.value})"
- )
- })
- .toBox
- .map(_ => srv)
+ } yield ()
}
}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/NodeInfoService.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/NodeInfoService.scala
index 5f7f23bcc3b..56e887fbc00 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/NodeInfoService.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/NodeInfoService.scala
@@ -37,39 +37,14 @@
package com.normation.rudder.services.nodes
-import com.normation.NamedZioLogger
-import com.normation.errors._
import com.normation.errors.IOResult
import com.normation.inventory.domain._
-import com.normation.inventory.ldap.core.InventoryDit
-import com.normation.inventory.ldap.core.InventoryMapper
import com.normation.inventory.ldap.core.LDAPConstants._
import com.normation.ldap.sdk._
-import com.normation.ldap.sdk.BuildFilter._
-import com.normation.ldap.sdk.LDAPConnectionProvider
-import com.normation.ldap.sdk.LDAPIOResult._
-import com.normation.ldap.sdk.syntax._
-import com.normation.rudder.domain.Constants
-import com.normation.rudder.domain.NodeDit
import com.normation.rudder.domain.RudderLDAPConstants._
-import com.normation.rudder.domain.logger.NodeLoggerPure
-import com.normation.rudder.domain.logger.TimingDebugLogger
-import com.normation.rudder.domain.logger.TimingDebugLoggerPure
import com.normation.rudder.domain.nodes.Node
import com.normation.rudder.domain.nodes.NodeInfo
-import com.normation.rudder.repository.CachedRepository
-import com.normation.rudder.repository.ldap.LDAPEntityMapper
-import com.normation.rudder.services.nodes.NodeInfoService.A_MOD_TIMESTAMP
-import com.normation.rudder.services.nodes.NodeInfoServiceCached.UpdatedNodeEntries
-import com.normation.rudder.services.nodes.NodeInfoServiceCached.buildInfoMaps
-import com.normation.zio._
-import com.unboundid.ldap.sdk._
import org.joda.time.DateTime
-import scala.collection.mutable.{Map => MutMap}
-import scala.collection.mutable.Buffer
-import scala.concurrent.duration.FiniteDuration
-import zio.{System => _, _}
-import zio.syntax._
/*
* General logic for the cache implementation of NodeInfo.
@@ -119,10 +94,8 @@ trait NodeInfoService {
/**
* Return the number of managed (ie non policy server, no rudder role nodes.
* Implementation of that method must as efficient as possible.
- * It can't fails (implementation must use a sane default if backend is not accessible,
- * or cache the information)
*/
- def getNumberOfManagedNodes: Int
+ def getNumberOfManagedNodes: IOResult[Int]
/**
* Get all node infos.
@@ -251,1014 +224,3 @@ final case class LocalNodeInfoCache(
lastModEntryCSN: Seq[String],
managedNodes: Int
)
-
-/*
- * Companion object of NodeInfoServiceCached where pure computing function are
- * put. Here, you will find:
- *
- * - the logic to build a `LDAPNodeInfo` from existing cache and partial updates,
- * -
- */
-object NodeInfoServiceCached {
- val logEffect = NodeLoggerPure.Cache.logEffect
-
- // utility class to move node updates around
- final case class NodeUpdates(
- updated: Buffer[LDAPNodeInfo] = Buffer(),
- nodeErrors: Buffer[String] =
- Buffer(), // that's actually nodeId, but we are in perf/mem sensitive part and avoid object instanciation
-
- containerErrors: Buffer[String] = Buffer() // that's actually a container DN
- )
- // utility class to move around entries (node, inventories, CSN) from LDAP
- final case class InfoMaps(
- // some map of things - mutable, yes
- nodes: MutMap[String, LDAPEntry] = MutMap(), // node_uuid -> entry
-
- nodeInventories: MutMap[String, LDAPEntry] = MutMap(), // node_uuid -> entry
-
- machineInventories: MutMap[String, LDAPEntry] = MutMap(), // machine_dn -> entry
-
- entriesCSN: Buffer[String] = Buffer()
- )
-
- /*
- * That method constructs new LDAPInfo entries from partial update on nodes and inventories (nodes and machines).
- * To keep consistency, each map is processed in turn, and we mark errors (an update on a node when node inventory is missing,
- * or an update on an inventory (node and machine) where the node is missing) appart.
- * These errors can happen when a node is in the middle of acceptation and only half of data are created in LDAP when
- * the cache is updated.
- */
- def constructNodesFromPartialUpdate(
- currentCache: LocalNodeInfoCache,
- infoMaps: InfoMaps
- ): NodeUpdates = {
-
- // get machine inventory from inventory map. it will lazely use cacheEntry if not found in machineInventories
- def getNonOptionnalMachineInventory(
- containerDn: String,
- machineInventories: MutMap[String, LDAPEntry], // only read
-
- managedMachineInventories: scala.collection.mutable.Buffer[String],
- cacheEntry: () => Option[LDAPEntry]
- ): Option[LDAPEntry] = {
- for {
- machineEntry <- machineInventories.get(containerDn) match {
- case Some(value) =>
- // machine inventory is handled
- managedMachineInventories += containerDn
- Some(value)
- case None => // look in cache
- cacheEntry()
- }
- } yield {
- machineEntry
- }
- }
-
- val managedNodeInventories = scala.collection.mutable.Buffer[String]()
- val managedMachineInventories = scala.collection.mutable.Buffer[String]()
- val result = NodeUpdates()
-
- // we have three loops with map/filter on entries
-
- /* loop1: for nodes
- * For each entry updated in ou=Nodes, we look if the inventories are
- * also updated. If so, we mark them as "done" to avoid work in following loops.
- * If not, we look in cache. If no inventory is in cache for that node, we may
- * be in the middle of an inventory addition, and mark the nodeId as "try to compensate with full lookup".
- */
- infoMaps.nodes.foreach {
- case (id, nodeEntry) =>
- infoMaps.nodeInventories.get(id) match {
- case Some(nodeInv) =>
- // Node inventory with this is handled, so we should not look for it again
- managedNodeInventories += id
- // if a containerDn is defined, we must find a node
- nodeInv(A_CONTAINER_DN) match {
- case Some(containerDn) =>
- val machineInv = getNonOptionnalMachineInventory(
- containerDn,
- infoMaps.machineInventories,
- managedMachineInventories,
- () => currentCache.nodeInfos.get(NodeId(id)).flatMap(_._1.machineEntry)
- )
- machineInv match {
- case Some(_) =>
- result.updated.addOne(LDAPNodeInfo(nodeEntry, nodeInv, machineInv))
- case None =>
- // container is defined, but not found. This is an error
- result.nodeErrors.addOne(id)
- }
- case None => // no container expected
- result.updated.addOne(LDAPNodeInfo(nodeEntry, nodeInv, None))
- }
- case None => // look in cache
- currentCache.nodeInfos.get(NodeId(id)) match {
- case None => // oups, mark as problem
- result.nodeErrors.addOne(id)
- case Some((ldapInfo, nodeInfo)) => // use that
- val nodeInv = ldapInfo.nodeInventoryEntry
- // if a containerDn is defined, we must find a node
- nodeInv(A_CONTAINER_DN) match {
- case Some(containerDn) =>
- val machineInv = getNonOptionnalMachineInventory(
- containerDn,
- infoMaps.machineInventories,
- managedMachineInventories,
- () => ldapInfo.machineEntry
- )
- machineInv match {
- case Some(_) =>
- result.updated.addOne(LDAPNodeInfo(nodeEntry, nodeInv, machineInv))
- case None =>
- // container is defined, but not found. This is an error
- result.nodeErrors.addOne(id)
- }
- case None => // no container expected
- result.updated.addOne(LDAPNodeInfo(nodeEntry, nodeInv, None))
- }
- }
-
- }
- }
- val nbNodeEntries = result.updated.size
- logEffect.trace(s"Constructing nodes from partial update")
- logEffect.trace(s" -- nodeEntries: ${result.updated.mkString(",")}")
-
- /*
- * Loop 2: for node inventories.
- * Only process node inventories that are not yet marked as done.
- * Again, we can have error: here, we know the node info for these inventories must
- * be in cache, since the one form updates were done in loop 1. If not in cache, it's
- * an error.
- */
- infoMaps.nodeInventories.foreach {
- case (id, nodeInv) =>
- if (!managedNodeInventories.contains(id)) {
- currentCache.nodeInfos.get(NodeId(id)) match {
- case None => // oups
- result.nodeErrors.addOne(id)
- case Some((ldapInfo, nodeInfo)) =>
- val nodeEntry = ldapInfo.nodeEntry
- nodeInv(A_CONTAINER_DN) match {
- case Some(containerDn) =>
- val machineInv = getNonOptionnalMachineInventory(
- containerDn,
- infoMaps.machineInventories,
- managedMachineInventories,
- () => ldapInfo.machineEntry
- )
- machineInv match {
- case Some(_) =>
- result.updated.addOne(LDAPNodeInfo(nodeEntry, nodeInv, machineInv))
- case None =>
- // container is defined, but not found. This is an error
- result.nodeErrors.addOne(id)
- }
- case None =>
- result.updated.addOne(LDAPNodeInfo(nodeEntry, nodeInv, None))
- }
- }
- }
- }
-
- val nbNodeInvs = result.updated.size - nbNodeEntries
- logEffect.trace(s" -- inventoryEntries: ${result.updated.iterator.drop(nbNodeEntries).mkString(",")}")
- logEffect.debug(s" -- following nodes were not complete for cache: ${result.nodeErrors.mkString(",")}")
-
- /* loop3: for machine inventories
- * Very similar than node inventories, for same reasons, but one machine can be mapped to several nodes!
- */
- infoMaps.machineInventories.foreach {
- case (containerDn, machineInv) =>
- if (!managedMachineInventories.contains(containerDn)) {
- // the tricky part: there may be several nodes with the same containerDn
- // now, we must have AT LEAST one value in res, else it's the same kind of error than a node inventory without a node.
- var atLeastOne = false
- currentCache.nodeInfos.collect {
- case (nodeId, (ldapInfo, _)) if (ldapInfo.nodeInventoryEntry(A_CONTAINER_DN).equals(Some(containerDn))) =>
- atLeastOne = true
- result.updated.addOne(LDAPNodeInfo(ldapInfo.nodeEntry, ldapInfo.nodeInventoryEntry, Some(machineInv)))
- }
- if (!atLeastOne) {
- result.containerErrors.addOne(containerDn)
- }
- }
- }
-
- logEffect.trace(s" -- machineInventoriesEntries: ${result.updated.iterator.drop(nbNodeEntries + nbNodeInvs).mkString(",")}")
- logEffect.debug(s" -- following machineInventories were not complete for cache: ${result.containerErrors.mkString(",")}")
-
- result
- }
-
- /*
- * Simpler version of `constructNodesFromPartialUpdate` where we assume that we have all datas,
- * so we can only loop one time for nodes and be done.
- */
- def constructNodesFromAllEntries(infoMaps: InfoMaps, checkRoot: Boolean = true): IOResult[NodeUpdates] = {
- for {
- results <- Ref.make(NodeUpdates())
- rootMissing <- Ref.make(checkRoot)
- // foreach cannot work on mutableMap
- _ <- ZIO.foreach(infoMaps.nodes: Iterable[(String, LDAPEntry)]) {
- case (id, nodeEntry) =>
- infoMaps.nodeInventories.get(id) match {
- case None =>
- // We can safely skip it - when the inventory will appear, it will be caught up in the partial update
- // For the case where we have an inventory but no node, it's the same
- // If partial update get only part of the object (inv, or node), then the nodeErrors will fetch it
- NodeLoggerPure.Cache.debug(
- s"Node with id '${id}' is in ou=Nodes,cn=rudder-configuration but doesn't have an inventory: skipping it"
- ) *>
- results.update { x => x.nodeErrors.addOne(id); x }
- case Some(nodeInv) =>
- val machineInv = for {
- containerDn <- nodeInv(A_CONTAINER_DN)
- machineEntry <- infoMaps.machineInventories.get(containerDn)
- } yield {
- machineEntry
- }
- results.update { x => x.updated.addOne(LDAPNodeInfo(nodeEntry, nodeInv, machineInv)); x } *>
- ZIO.when(id == Constants.ROOT_POLICY_SERVER_ID.value)(rootMissing.set(false))
- }
- }
- // here, we must ensure that root ID is on the list, else chaos ensue.
- // If root is missing, invalidate the case
- _ <- ZIO.whenZIO(rootMissing.get) {
- val msg = {
- "'root' node is missing from the list of nodes. Rudder can not work in that state. We clear the cache now to try" +
- "to auto-correct the problem. If it persists, try to run 'rudder agent inventory && rudder agent run' " +
- "from the root server and check /var/log/rudder/webapp/ logs for additionnal information."
- }
- NodeLoggerPure.Cache.error(msg) *> msg.fail
- }
- ldapNodes <- results.get
- } yield {
- ldapNodes
- }
- }
-
- // utility class to move around deleted and existing/updated node entries
- final case class UpdatedNodeEntries(
- deleted: Seq[LDAPEntry],
- updated: Seq[LDAPEntry]
- )
- // logic to sort out different kind of node entries from generic LDAP entries
- def buildInfoMaps(updatedNodeEntries: UpdatedNodeEntries, currentLastModif: DateTime): (InfoMaps, DateTime) = {
- val infoMaps = InfoMaps(MutMap.empty, MutMap.empty, MutMap.empty, Buffer())
- val t0 = System.currentTimeMillis
-
- // two vars to keep track of the new last modification time and entries csn
- var lastModif = currentLastModif
-
- // look for the maxed timestamp
- (updatedNodeEntries.deleted ++ updatedNodeEntries.updated).foreach { e =>
- e.getAsGTime(A_MOD_TIMESTAMP) match {
- case None => // nothing
- case Some(x) =>
- if (x.dateTime.isAfter(lastModif)) {
- lastModif = x.dateTime
- infoMaps.entriesCSN.clear()
- }
- if (x.dateTime == lastModif) {
- e("entryCSN").map(csn => infoMaps.entriesCSN.append(csn))
- }
- }
- }
-
- // now, create the nodeInfo
- updatedNodeEntries.updated.foreach { e =>
- if (e.isA(OC_MACHINE)) {
- infoMaps.machineInventories += (e.dn.toString -> e)
- } else if (e.isA(OC_NODE)) {
- infoMaps.nodeInventories += (e.value_!(A_NODE_UUID) -> e)
- } else if (e.isA(OC_RUDDER_NODE)) {
- infoMaps.nodes += (e.value_!(A_NODE_UUID) -> e)
- } else {
- // it's an error, don't use
- }
- }
-
- val t1 = System.currentTimeMillis
- NodeLoggerPure.Cache.trace(
- s"Updated entries are machineInventories: ${infoMaps.machineInventories.mkString(",")} \n" +
- s"nodeInventories: ${infoMaps.nodeInventories.mkString(",")} \n" +
- s"nodes: ${infoMaps.nodes.mkString(",")} "
- )
- TimingDebugLogger.debug(s"Getting node info entries: ${t1 - t0}ms")
- (infoMaps, lastModif)
- }
-}
-
-trait NodeInfoServiceCached extends NodeInfoService with NamedZioLogger with CachedRepository {
- import NodeInfoService._
-
- def ldap: LDAPConnectionProvider[RoLDAPConnection]
- def nodeDit: NodeDit
- def inventoryDit: InventoryDit
- def removedDit: InventoryDit
- def pendingDit: InventoryDit
- def ldapMapper: LDAPEntityMapper
- def inventoryMapper: InventoryMapper
-
- override def loggerName: String = this.getClass.getName
-
- val semaphore = Semaphore.make(1).runNow
- /*
- * Compare if cache is up to date (based on internal state of the cache)
- */
- def isUpToDate(): IOResult[Boolean] = {
- IOResult.attempt(nodeCache).flatMap {
- case Some(cache) => checkUpToDate(cache.lastModTime, cache.lastModEntryCSN)
- case None => false.succeed // an empty cache is never up to date
- }
- }
-
- /**
- * Check is LDAP directory contains updated entries compare
- * to the date we pass in arguments.
- * Entries may be any entry relevant for our cache, in particular,
- * some attention must be provided to deleted entries.
- */
- protected[this] def checkUpToDate(lastKnowModification: DateTime, lastModEntryCSN: Seq[String]): IOResult[Boolean]
-
- /**
- * This method must return only and all entries under:
- * - ou=Nodes,
- * - ou=[Node, Machine], ou=Accepted Inventories, etc
- *
- * attributes is the list of attributes needed in returned entries
- */
- def getNodeInfoEntries(
- con: RoLDAPConnection,
- attributes: Seq[String],
- status: InventoryStatus,
- lastModification: Option[DateTime]
- ): LDAPIOResult[Seq[LDAPEntry]]
-
- /*
- * Retrieve from backend LDANodeInfo for the entries
- */
- def getBackendLdapNodeInfo(nodeIds: Seq[String]): IOResult[Seq[LDAPNodeInfo]]
-
- // containerDn look like machineId=000f6268-e825-d13c-fa14-f9e55d05038c,ou=Machines,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration
- def getBackendLdapContainerinfo(containersDn: Seq[String]): IOResult[Seq[LDAPNodeInfo]]
-
- override def getNumberOfManagedNodes: Int = nodeCache.map(_.managedNodes).getOrElse(0)
-
- /*
- * Our cache
- */
- protected[this] var nodeCache = Option.empty[LocalNodeInfoCache]
-
- // we need modifyTimestamp to search for update and entryCSN to remove already processed entries
- private[this] val searchAttributes = nodeInfoAttributes :+ A_MOD_TIMESTAMP :+ "entryCSN"
-
- /**
- * Remove a node from cache. It cannot fail - if node is not in cache, it is a success
- */
- def removeNodeFromCache(nodeId: NodeId): IOResult[Unit] = {
- semaphore.withPermit(
- IOResult.attempt({ nodeCache = nodeCache.map(x => x.copy(nodeInfos = x.nodeInfos.removed(nodeId))) })
- )
- }
-
- /**
- * Update cache, without doing anything with the data
- */
- def updateCache(): IOResult[Unit] = {
- withUpToDateCache("update cache")(_ => ZIO.unit)
- }
-
- /**
- * That's the method that do all the logic
- */
- private[this] def withUpToDateCache[T](
- label: String
- )(useCache: Map[NodeId, (LDAPNodeInfo, NodeInfo)] => IOResult[T]): IOResult[T] = {
- /*
- * Get all relevant info from backend along with the
- * date of the last modification.
- * If `existingCache` is None, all data will be fetched to fully build a fresh new cache.
- * If `existingCache` is Some(cache), then only a partial update will be computed based on
- * modified entries since cache last update datetime.
- *
- * That method is not threadsafe and must be enclosed in a semaphore or other guarding mean.
- */
- def getDataFromBackend(existingCache: Option[LocalNodeInfoCache]): IOResult[LocalNodeInfoCache] = {
-
- // Find entries modified since `lastKnowModification` (or all entries if None)
- def getUpdatedDataIO(lastKnowModification: Option[DateTime]): IOResult[UpdatedNodeEntries] = {
- val filter = lastKnowModification match {
- case Some(date) => AND(IS(OC_NODE), GTEQ(A_MOD_TIMESTAMP, GeneralizedTime(date).toString))
- case None => AND(IS(OC_NODE))
- }
-
- for {
- con <- ldap
- t0 <- currentTimeMillis
- deleted <- con.search(removedDit.NODES.dn, One, filter, A_MOD_TIMESTAMP, "entryCSN")
- t1 <- currentTimeMillis
- updated <- getNodeInfoEntries(con, searchAttributes, AcceptedInventory, lastKnowModification)
- t2 <- currentTimeMillis
- } yield {
- TimingDebugLogger.debug(
- s"Getting updated node info data from LDAP: ${t2 - t0}ms total (${t1 - t0}ms for removed nodes, ${t2 - t1} for accepted inventories)"
- )
- UpdatedNodeEntries(deleted, updated)
- }
- }
-
- // create a fresh new cache with `updatedEntries`. If `existingCache` is None, we assume these entries are all
- // available entries, else we assume these are only the ones updated since cache last update.
- def getUpdatedCache(
- existingCache: Option[LocalNodeInfoCache],
- updatedEntries: UpdatedNodeEntries
- ): IOResult[(LocalNodeInfoCache, Int)] = {
- // if we don't have an existing cache, then we are (re)initializing cache, so we get all entries since epoch
- val (infoMaps, lastModif) = buildInfoMaps(updatedEntries, existingCache.map(_.lastModTime).getOrElse(new DateTime(0)))
-
- for {
- updates <- existingCache match {
- case None => // full build
- NodeInfoServiceCached.constructNodesFromAllEntries(infoMaps)
- case Some(cache) => // only update what need to be updated
- NodeInfoServiceCached.constructNodesFromPartialUpdate(cache, infoMaps).succeed
- }
- // try to compasente for errors: for node id, get full info from backend, for containers, dedicated search (todo)
- _ <- NodeLoggerPure.Cache.debug(s"Found ${updates.updated.size} new entries to update cache")
- compensate <- if (updates.nodeErrors.nonEmpty) {
- getBackendLdapNodeInfo(updates.nodeErrors.toSeq).catchAll(
- err => { // we don't want to fail because we tried to compensate
- NodeLoggerPure.Cache.warn(
- s"Error when trying to find in LDAP node entries: ${updates.nodeErrors.mkString(", ")}: ${err.fullMsg}"
- ) *> Seq().succeed
- }
- )
- } else {
- Seq().succeed
- }
- _ <- ZIO.when(updates.nodeErrors.size > 0) {
- NodeLoggerPure.Cache.debug(s"${updates.nodeErrors.size} were in errors, compensated ${compensate.size}")
- }
- compensateContainer <- if (updates.containerErrors.nonEmpty) {
- getBackendLdapContainerinfo(updates.containerErrors.toSeq).catchAll(
- err => { // we don't want to fail because we tried to compensate
- NodeLoggerPure.Cache.warn(
- s"Error when trying to find in LDAP containers entries: ${updates.containerErrors
- .mkString(", ")}: ${err.fullMsg}"
- ) *> Seq().succeed
- }
- )
- } else {
- Seq().succeed
- }
- _ <- ZIO.when(updates.containerErrors.size > 0) {
- NodeLoggerPure.Cache.debug(
- s"${updates.containerErrors.size} were in errors, compensated ${compensateContainer.size}"
- )
- }
- // now construct the nodeInfo
- updated <- ZIO.foreach((updates.updated ++ compensate ++ compensateContainer): Iterable[LDAPNodeInfo]) { ldapNode =>
- val id = ldapNode.nodeEntry.value_!(A_NODE_UUID) // id is mandatory
- ldapMapper
- .convertEntriesToNodeInfos(
- ldapNode.nodeEntry,
- ldapNode.nodeInventoryEntry,
- ldapNode.machineEntry
- )
- .foldZIO(
- err =>
- NodeLoggerPure.Cache.error(
- s"An error occured while updating node cache: can not unserialize node with id '${id}', it will be ignored: ${err.fullMsg}"
- ) *> None.succeed,
- nodeInfo => Some((nodeInfo.id, (ldapNode, nodeInfo))).succeed
- )
- }
- _ <-
- NodeLoggerPure.Cache.trace(s"Updated entries are updatedNodeInfos: ${updated.flatten.map(_._1.value).mkString(", ")}")
- } yield {
- val allEntries = existingCache.map(_.nodeInfos).getOrElse(Map()) ++ updated.flatten.toMap
- val cache = LocalNodeInfoCache(
- allEntries,
- lastModif,
- infoMaps.entriesCSN.toSeq,
- allEntries.filter { case (_, (_, n)) => !n.isPolicyServer }.size
- )
- (cache, infoMaps.nodes.size)
- }
- }
-
- for {
- t0 <- currentTimeMillis
- data <- getUpdatedDataIO(existingCache.map(_.lastModTime))
- res <- getUpdatedCache(existingCache, data)
- (cache, nb) = res
- t1 <- currentTimeMillis
- _ <- TimingDebugLoggerPure.debug(s"Converting ${nb} node info entries to node info: ${t1 - t0}ms")
- } yield {
- cache
- }
- }
-
- // Here finaly comes the whole logic that
- for {
- // only checking the cache validity should be in a semaphore - logic to read it does not need to
- // we cannot get (cache, t0), because it fails with Cannot prove that NoSuchElementException <:< com.normation.errors.RudderError
- result <-
- semaphore.withPermit(
- for {
- t0 <- currentTimeMillis
- updatedCache <- nodeCache match {
- case None =>
- for {
- updated <- getDataFromBackend(None).foldZIO(
- err =>
- IOResult.attempt({ nodeCache = None; () }) *> Chained(
- "Could not get node information from database",
- err
- ).fail,
- newCache => {
- logPure.debug(
- s"NodeInfo cache is now initialized, last modification time: '${newCache.lastModTime}', last cache update:" +
- s" with ${newCache.nodeInfos.size} entries"
- ) *>
- logPure
- .trace(s"NodeInfo cache initialized entries: [${newCache.nodeInfos.keySet
- .map(_.value)
- .mkString(", ")}]") *>
- IOResult.attempt({ nodeCache = Some(newCache); () }) *>
- newCache.succeed
- }
- )
- } yield {
- updated
- }
-
- case Some(currentCache) =>
- isUpToDate().flatMap(isClean => {
- if (!isClean) {
- for {
- updated <- getDataFromBackend(Some(currentCache)).foldZIO(
- err =>
- IOResult.attempt({
- nodeCache = None; ()
- }) *> Chained(
- "Could not get updated node information from database",
- err
- ).fail,
- newCache => {
- logPure.debug(
- s"NodeInfo cache is not up to date, last modification time: '${newCache.lastModTime}', last cache update:" +
- s" '${currentCache.lastModTime}' => updating cache with ${newCache.nodeInfos.size} entries"
- ) *>
- logPure.trace(
- s"NodeInfo cache updated entries: [${newCache.nodeInfos.keySet.map {
- _.value
- }.mkString(", ")}]"
- ) *>
- IOResult.attempt({
- nodeCache = Some(newCache); ()
- }) *>
- newCache.succeed
- }
- )
- } yield {
- updated
- }
- } else {
- logPure.debug(s"NodeInfo cache is up to date, ${nodeCache
- .map(c => s"last modification time: '${c.lastModTime}' for: '${c.lastModEntryCSN.mkString("','")}'")
- .getOrElse("")}") *>
- currentCache.succeed
- }
- })
- }
-
- t1 <- currentTimeMillis
- _ <- IOResult.attempt(TimingDebugLogger.debug(s"Get cache for node info (${label}): ${t1 - t0}ms"))
- } yield {
- (t0, updatedCache)
- }
- )
- (t0, updatedCache) = result
- t1 <- currentTimeMillis
- res <- useCache(updatedCache.nodeInfos) // this does not need to be in a semaphore
- t2 <- currentTimeMillis
- _ <- IOResult.attempt(
- TimingDebugLogger.debug(s"Get node info (${label}): ${t2 - t0}ms - exploring the cache took ${t2 - t1}ms ")
- )
- } yield {
- res
- }
- }
-
- /**
- * An utility method that gets data from backend for things that are
- * node really nodes (pending or deleted).
- */
- private[this] def getNotAcceptedNodeDataFromBackend(status: InventoryStatus): IOResult[Map[NodeId, NodeInfo]] = {
- import scala.collection.mutable.{Map => MutMap}
-
- for {
- con <- ldap
- allEntries <- getNodeInfoEntries(con, searchAttributes, status, None)
- res <- {
- // some map of things - mutable, yes
- val nodeInventories = MutMap[String, LDAPEntry]() // node_uuid -> entry
- val machineInventories = MutMap[String, LDAPEntry]() // machine_dn -> entry
-
- // now, create the nodeInfo
- allEntries.foreach { e =>
- if (e.isA(OC_MACHINE)) {
- machineInventories += (e.dn.toString -> e)
- } else if (e.isA(OC_NODE)) {
- nodeInventories += (e.value_!(A_NODE_UUID) -> e)
- } else {
- // it's an error, don't use
- }
- }
-
- ZIO.foreach(nodeInventories.toMap) {
- case (id, nodeEntry) =>
- val machineInfo = for {
- containerDn <- nodeEntry(A_CONTAINER_DN)
- machineEntry <- machineInventories.get(containerDn)
- } yield {
- machineEntry
- }
- for {
- nodeInfo <- ldapMapper.convertEntriesToSpecialNodeInfos(nodeEntry, machineInfo)
- } yield {
- (nodeInfo.id, nodeInfo)
- }
- }
- }
- } yield {
- res
- }
- }
-
- private[this] def getNotAcceptedNodeInfo(nodeId: NodeId, status: InventoryStatus): IOResult[Option[NodeInfo]] = {
- val dit = status match {
- case AcceptedInventory => inventoryDit
- case PendingInventory => pendingDit
- case RemovedInventory => removedDit
- }
-
- for {
- con <- ldap
- optNodeEntry <- con.get(dit.NODES.NODE.dn(nodeId), searchAttributes: _*)
- nodeInfo <- (optNodeEntry match {
- case None => None.succeed
- case Some(nodeEntry) =>
- nodeEntry.getAsDn(A_CONTAINER_DN) match {
- case None => None.succeed
- case Some(dn) =>
- for {
- machineEntry <- con.get(dn, searchAttributes: _*)
- nodeInfo <- ldapMapper.convertEntriesToSpecialNodeInfos(nodeEntry, machineEntry)
- } yield {
- Some(nodeInfo)
- }
- }
- })
- } yield {
- nodeInfo
- }
- }
-
- final override def getPendingNodeInfos(): IOResult[Map[NodeId, NodeInfo]] = getNotAcceptedNodeDataFromBackend(PendingInventory)
- final override def getDeletedNodeInfos(): IOResult[Map[NodeId, NodeInfo]] = getNotAcceptedNodeDataFromBackend(RemovedInventory)
- final override def getPendingNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] =
- getNotAcceptedNodeInfo(nodeId, PendingInventory)
- final override def getDeletedNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] =
- getNotAcceptedNodeInfo(nodeId, RemovedInventory)
-
- /**
- * Clear cache.
- */
- override def clearCache(): Unit = {
- semaphore
- .withPermit(
- (this.nodeCache = None).succeed
- )
- .runNow
- }
-
- // return the cache last update time, or epoch if cache is not init
- def getCacheLastUpdate: UIO[DateTime] = {
- semaphore.withPermit(ZIO.succeed(this.nodeCache.map(_.lastModTime).getOrElse(new DateTime(0))))
- }
-
- def getAll(): IOResult[Map[NodeId, NodeInfo]] = withUpToDateCache("all nodes info") { cache =>
- cache.view.mapValues(_._2).toMap.succeed
- }
- def getAllNodesIds(): IOResult[Set[NodeId]] = withUpToDateCache("all nodes id")(cache => cache.keySet.succeed)
- def getAllSystemNodeIds(): IOResult[Seq[NodeId]] = withUpToDateCache("all system nodes") { cache =>
- cache.collect { case (k, (_, x)) if (x.isPolicyServer) => k }.toSeq.succeed
- }
-
- def getAllNodes(): IOResult[Map[NodeId, Node]] = withUpToDateCache("all nodes") { cache =>
- cache.view.mapValues(_._2.node).toMap.succeed
- }
-
- def getAllNodeInfos(): IOResult[Seq[NodeInfo]] = withUpToDateCache("all nodeinfos") { cache =>
- cache.view.values.map(_._2).toSeq.succeed
- }
-
- def getNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = withUpToDateCache(s"${nodeId.value} node info") { cache =>
- cache.get(nodeId).map(_._2).succeed
- }
-
- def getNodeInfos(nodeIds: Set[NodeId]): IOResult[Set[NodeInfo]] = withUpToDateCache(s"${nodeIds.size} nodes infos") { cache =>
- cache.filter(x => nodeIds.contains(x._1)).values.map(_._2).toSet.succeed
- }
-
- def getNodeInfosSeq(nodeIds: Seq[NodeId]): IOResult[Seq[NodeInfo]] = withUpToDateCache(s"${nodeIds.size} nodes infos") {
- cache => nodeIds.map(id => cache.get(id).map(_._2)).flatten.succeed
- }
-}
-
-/**
- * A testing implementation, that just retrieve node info each time. Not very efficient.
- */
-class NaiveNodeInfoServiceCachedImpl(
- override val ldap: LDAPConnectionProvider[RoLDAPConnection],
- override val nodeDit: NodeDit,
- override val inventoryDit: InventoryDit,
- override val removedDit: InventoryDit,
- override val pendingDit: InventoryDit,
- override val ldapMapper: LDAPEntityMapper,
- override val inventoryMapper: InventoryMapper
-) extends NodeInfoServiceCached {
-
- override def loggerName: String = this.getClass.getName
-
- override def checkUpToDate(lastKnowModification: DateTime, lastModEntryCSN: Seq[String]): IOResult[Boolean] = {
- false.succeed // yes naive
- }
-
- def getNewNodeInfoEntries(
- con: RoLDAPConnection,
- lastKnowModification: DateTime,
- searchAttributes: Seq[String]
- ): LDAPIOResult[Seq[LDAPEntry]] = ???
-
- /**
- * This method must return only and all entries under:
- * - ou=Nodes,
- * - ou=[Node, Machine], ou=Accepted Inventories, etc
- */
- override def getNodeInfoEntries(
- con: RoLDAPConnection,
- searchAttributes: Seq[String],
- status: InventoryStatus,
- lastModification: Option[DateTime]
- ): LDAPIOResult[Seq[LDAPEntry]] = {
- for {
- nodeInvs <- con.search(inventoryDit.NODES.dn, One, BuildFilter.ALL, searchAttributes: _*)
- machineInvs <- con.search(inventoryDit.MACHINES.dn, One, BuildFilter.ALL, searchAttributes: _*)
- nodes <- if (status == AcceptedInventory) {
- con.search(nodeDit.NODES.dn, One, BuildFilter.ALL, searchAttributes: _*)
- } else {
- Seq().succeed
- }
- } yield {
- nodeInvs ++ machineInvs ++ nodes
- }
- }
- // necessary for tests
- override def getBackendLdapNodeInfo(nodeIds: Seq[String]): IOResult[Seq[LDAPNodeInfo]] = {
- Seq().succeed
- }
-
- override def getBackendLdapContainerinfo(containersDn: Seq[String]): IOResult[Seq[LDAPNodeInfo]] = ???
-}
-
-/**
- * A cache on top of node info service.
- *
- */
-
-class NodeInfoServiceCachedImpl(
- override val ldap: LDAPConnectionProvider[RoLDAPConnection],
- override val nodeDit: NodeDit,
- override val inventoryDit: InventoryDit,
- override val removedDit: InventoryDit,
- override val pendingDit: InventoryDit,
- override val ldapMapper: LDAPEntityMapper,
- override val inventoryMapper: InventoryMapper,
- minimumCacheValidity: FiniteDuration
-) extends NodeInfoServiceCached {
- import NodeInfoService._
- val minimumCacheValidityMillis = minimumCacheValidity.toMillis
-
- override def loggerName: String = this.getClass.getName
-
- /*
- * Check if node related infos are up to date.
- *
- * Here, we need to only check for attributeModifyTimestamp
- * under ou=AcceptedInventories and under ou=Nodes (onelevel)
- * and only for machines, inventory nodes, and node,
- * and inventory nodes under ou=RemovedInventories
- * Reason:
- * - only these three entries are used for node info (and none of their
- * sub-entries)
- * - if a node is deleted, it either go to RemovedInventories (and so the
- * machine, but we don't care as soon as we know a node went there) and we
- * will see its based on modify timestamps, or if "node full erase" is enabled,
- * a special call to "remove node from cache" must be done
- * - for ou=Node, all modifications happen in the entry, so the modifyTimestamp
- * is changed accordingly.
- *
- * Moreover, it is less costly to only do one search and post-filter result
- * than to do 2 or more.
- *
- * We also need to filter out entry in the previous last modify set to not
- * have them always matching. The reason is that modifyTimestamp is on
- * second. But we can have several modify in a second. If we get our
- * lastModificationTimestamp in just before the second modification, that
- * modification will be ignore forever (or at least until an other modification
- * happens - see ticket https://www.rudder-project.org/redmine/issues/12988)
- * The filter is based on entryCSN, which is sure to be unique (by def).
- *
- * Finally, we limit the result to one, because here, we just need to know if
- * some update exists, not WHAT they are, nor the MOST RECENT one. Just that
- * at least one exists.
- *
- * A cleaner implementation could use a two persistent search which would notify
- * when a cache becomes invalid and reset it, but the rational for that implementation is:
- * - it's extremely simple to understand the logic (if(cache is up-to-date) use it else update cache)
- * - most of the time (99.99% of it), the search will return 0 result and will be cache on OpenLDAP,
- * whatever the number of entries. So we talking of a request taking a couple of ms on the server
- * (with a vagrant VM on the same host (so, almost no network), it takes from client to server and
- * back ~10ms on a dev machine.
- */
- override def checkUpToDate(lastKnowModification: DateTime, lastModEntryCSN: Seq[String]): IOResult[Boolean] = {
- ZIO.succeed(System.currentTimeMillis).flatMap { n0 =>
- // if last check is less than 100 ms ago, consider cache ok
- if (n0 - lastKnowModification.getMillis < minimumCacheValidityMillis) {
- true.succeed
- } else {
- val searchRequest = new SearchRequest(
- nodeDit.BASE_DN.toString,
- Sub.toUnboundid,
- DereferencePolicy.NEVER,
- 1,
- 0,
- false,
- AND(
- OR(
- // ou=Removed Inventories,ou=Inventories,cn=rudder-configuration
- AND(
- IS(OC_NODE),
- Filter.create(s"entryDN:dnOneLevelMatch:=${removedDit.NODES.dn.toString}")
- ), // ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration
-
- AND(IS(OC_NODE), Filter.create(s"entryDN:dnOneLevelMatch:=${inventoryDit.NODES.dn.toString}")),
- AND(
- IS(OC_MACHINE),
- Filter.create(s"entryDN:dnOneLevelMatch:=${inventoryDit.MACHINES.dn.toString}")
- ), // ou=Nodes,cn=rudder-configuration - the objectClass is used only here
-
- AND(IS(OC_RUDDER_NODE), Filter.create(s"entryDN:dnOneLevelMatch:=${nodeDit.NODES.dn.toString}"))
- ),
- GTEQ(A_MOD_TIMESTAMP, GeneralizedTime(lastKnowModification).toString),
- NOT(OR(lastModEntryCSN.map(csn => EQ("entryCSN", csn)): _*))
- ),
- "1.1"
- )
-
- for {
- con <- ldap
- entries <- // here, I have to rely on low-level LDAP connection, because I need to proceed size-limit exceeded as OK
- (ZIO.attempt(con.backed.search(searchRequest).getSearchEntries) catchAll {
- case e: LDAPSearchException if (e.getResultCode == ResultCode.SIZE_LIMIT_EXCEEDED) =>
- e.getSearchEntries().succeed
- case e: Throwable =>
- SystemError("Error when searching node information", e).fail
- }).foldZIO(
- err =>
- logPure.debug(
- s"Error when checking for cache expiration: invalidating it. Error was: ${err.fullMsg}"
- ) *> false.succeed,
- seq => {
- // we only have interesting entries in the result, so it's up to date if we have exactly 0 entries
- val res = seq.isEmpty
- logPure.trace(s"Cache check for node info gave '${res}' (${seq.size} entry returned)") *> res.succeed
- }
- )
- n1 <- ZIO.succeed(System.currentTimeMillis)
- _ <- IOResult.attempt(TimingDebugLogger.debug(s"Cache for nodes info expire ?: ${n1 - n0}ms"))
- } yield {
- entries
- }
- }
- }
- }
-
- /**
- * This method must return only and all entries under:
- * - ou=Nodes,
- * - ou=[Node, Machine], ou=Accepted Inventories, etc
- */
- override def getNodeInfoEntries(
- con: RoLDAPConnection,
- searchAttributes: Seq[String],
- status: InventoryStatus,
- lastModification: Option[DateTime]
- ): LDAPIOResult[Seq[LDAPEntry]] = {
- val dit = status match {
- case AcceptedInventory => inventoryDit
- case PendingInventory => pendingDit
- case RemovedInventory => removedDit
- }
-
- val filterNodes = OR(
- Seq(
- AND(IS(OC_NODE), Filter.create(s"entryDN:dnOneLevelMatch:=${dit.NODES.dn.toString}")),
- AND(IS(OC_MACHINE), Filter.create(s"entryDN:dnOneLevelMatch:=${dit.MACHINES.dn.toString}"))
- ) ++ (if (status == AcceptedInventory) {
- Seq(AND(IS(OC_RUDDER_NODE), Filter.create(s"entryDN:dnOneLevelMatch:=${nodeDit.NODES.dn.toString}")))
- } else {
- Seq()
- }): _*
- )
-
- val filter = lastModification match {
- case None => filterNodes
- case Some(d) => AND(filterNodes, GTEQ(A_MOD_TIMESTAMP, GeneralizedTime(d).toString))
- }
-
- con.search(nodeDit.BASE_DN, Sub, filter, searchAttributes: _*)
- }
-
- // Utility method to construct infomaps for getBackEnd methods
- private[this] def constructInfoMaps(nodeEntries: Seq[LDAPEntry], nodeInvs: Seq[LDAPEntry], machineInvs: Seq[LDAPEntry]) = {
- val res = NodeInfoServiceCached.InfoMaps()
- nodeEntries.foreach(e => res.nodes.addOne((e.value_!(A_NODE_UUID), e)))
- nodeInvs.foreach(e => res.nodeInventories.addOne((e.value_!(A_NODE_UUID), e)))
- machineInvs.foreach(e => res.machineInventories.addOne((e.dn.toString, e)))
- res
- }
-
- override def getBackendLdapNodeInfo(nodeIds: Seq[String]): IOResult[Seq[LDAPNodeInfo]] = {
- for {
- con <- ldap
- nodeEntries <-
- con.search(nodeDit.NODES.dn, One, OR(nodeIds.map(id => EQ(A_NODE_UUID, id)): _*), NodeInfoService.nodeInfoAttributes: _*)
- nodeInvs <- con.search(
- inventoryDit.NODES.dn,
- One,
- OR(nodeIds.map(id => EQ(A_NODE_UUID, id)): _*),
- NodeInfoService.nodeInfoAttributes: _*
- )
- containers = nodeInvs.flatMap(e => e(A_CONTAINER_DN).map(dn => new DN(dn).getRDN.getAttributeValues()(0)))
- machineInvs <- con.search(
- inventoryDit.MACHINES.dn,
- One,
- OR(containers.map(id => EQ(A_MACHINE_UUID, id)): _*),
- NodeInfoService.nodeInfoAttributes: _*
- )
- infoMaps <- IOResult.attempt(constructInfoMaps(nodeEntries, nodeInvs, machineInvs))
- res <- NodeInfoServiceCached.constructNodesFromAllEntries(infoMaps, checkRoot = false)
- } yield {
- // here, we ignore error cases
- res.updated.toSeq
- }
- }
-
- // containerDn look like machineId=000f6268-e825-d13c-fa14-f9e55d05038c,ou=Machines,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration
- override def getBackendLdapContainerinfo(containersDn: Seq[String]): IOResult[Seq[LDAPNodeInfo]] = {
- for {
- con <- ldap
- containers = containersDn.map(dn =>
- new DN(dn).getRDN.getAttributeValues()(0)
- ) // I'm using the same logic as up so that I can get all machines in one query, rather than on get per dn
- machineInvs <- con.search(
- inventoryDit.MACHINES.dn,
- One,
- OR(containers.map(id => EQ(A_MACHINE_UUID, id)): _*),
- NodeInfoService.nodeInfoAttributes: _*
- )
- nodeInvs <- con.search(
- inventoryDit.NODES.dn,
- One,
- OR(containersDn.map(container => EQ(A_CONTAINER_DN, container)): _*),
- NodeInfoService.nodeInfoAttributes: _*
- )
- nodeIds = nodeInvs.flatMap(_(A_NODE_UUID))
- nodeEntries <-
- con.search(nodeDit.NODES.dn, One, OR(nodeIds.map(id => EQ(A_NODE_UUID, id)): _*), NodeInfoService.nodeInfoAttributes: _*)
- infoMaps <- IOResult.attempt(constructInfoMaps(nodeEntries, nodeInvs, machineInvs))
- res <- NodeInfoServiceCached.constructNodesFromAllEntries(infoMaps, checkRoot = false)
- } yield {
- // here, we ignore error cases
- res.updated.toSeq
- }
- }
-}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/RemoveNodeService.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/RemoveNodeService.scala
index 5dab2cf6869..9f7aad6132c 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/RemoveNodeService.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/RemoveNodeService.scala
@@ -38,7 +38,6 @@ package com.normation.rudder.services.servers
import com.normation.box._
import com.normation.errors._
-import com.normation.eventlog.EventActor
import com.normation.eventlog.ModificationId
import com.normation.inventory.domain.AcceptedInventory
import com.normation.inventory.domain.AgentType
@@ -61,25 +60,20 @@ import com.normation.rudder.domain.logger.NodeLoggerPure
import com.normation.rudder.domain.nodes.Node
import com.normation.rudder.domain.nodes.NodeInfo
import com.normation.rudder.domain.nodes.NodeState
-import com.normation.rudder.facts.nodes.NodeFactStorage
+import com.normation.rudder.facts.nodes.ChangeContext
+import com.normation.rudder.facts.nodes.NodeFactRepository
+import com.normation.rudder.facts.nodes.SelectNodeStatus
import com.normation.rudder.hooks.HookEnvPairs
import com.normation.rudder.hooks.HookReturnCode
import com.normation.rudder.hooks.RunHooks
import com.normation.rudder.reports.ReportingConfiguration
-import com.normation.rudder.repository.EventLogRepository
import com.normation.rudder.repository.RoNodeGroupRepository
import com.normation.rudder.repository.UpdateExpectedReportsRepository
import com.normation.rudder.repository.WoNodeGroupRepository
import com.normation.rudder.repository.ldap.ScalaReadWriteLock
import com.normation.rudder.services.nodes.NodeInfoService
-import com.normation.rudder.services.nodes.NodeInfoServiceCached
-import com.normation.rudder.services.nodes.history.impl.InventoryHistoryJdbcRepository
import com.normation.rudder.services.policies.write.NodePoliciesPaths
import com.normation.rudder.services.policies.write.PathComputer
-import com.normation.rudder.services.reports.CacheComplianceQueueAction.ExpectedReportAction
-import com.normation.rudder.services.reports.CachedFindRuleNodeStatusReports
-import com.normation.rudder.services.reports.CachedNodeConfigurationService
-import com.normation.rudder.services.reports.CacheExpectedReportAction.RemoveNodeInCache
import com.normation.rudder.services.servers.DeletionResult._
import com.normation.utils.StringUuidGenerator
import com.normation.zio._
@@ -128,12 +122,18 @@ object DeleteMode {
/*
* Unitary post-deletion action. They happen once the node is actually deleted and eventlog saved.
* Typically, done for cleaning things or notifying other parts of rudder.
+ *
+ * We keep here action that should be executed each time we call delete, whatever if the node
+ * was really deleted.
+ * The other post delete hooks should be migration to CoreNodeFactRepository callbacks.
*/
trait PostNodeDeleteAction {
// a node can have several status (if inventories already deleted, and now in pending again for ex)
// or zero (if only some things remain)
// and if can optionnally have a nodeInfo
- def run(nodeId: NodeId, mode: DeleteMode, info: Option[NodeInfo], status: Set[InventoryStatus], actor: EventActor): UIO[Unit]
+ def run(nodeId: NodeId, mode: DeleteMode, info: Option[NodeInfo], status: Set[InventoryStatus])(implicit
+ cc: ChangeContext
+ ): UIO[Unit]
}
object PostNodeDeleteAction {
@@ -159,11 +159,11 @@ trait RemoveNodeService {
* - move the node
*/
- def removeNode(nodeId: NodeId, modId: ModificationId, actor: EventActor): Box[DeletionResult] = {
- removeNodePure(nodeId, DeleteMode.MoveToRemoved, modId, actor).map(_ => Success).toBox
+ def removeNode(nodeId: NodeId)(implicit cc: ChangeContext): Box[DeletionResult] = {
+ removeNodePure(nodeId, DeleteMode.MoveToRemoved).map(_ => Success).toBox
}
- def removeNodePure(nodeId: NodeId, mode: DeleteMode, modId: ModificationId, actor: EventActor): IOResult[NodeInfo]
+ def removeNodePure(nodeId: NodeId, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[NodeInfo]
}
trait RemoveNodeBackend {
@@ -172,11 +172,30 @@ trait RemoveNodeBackend {
def findNodeStatuses(nodeId: NodeId): IOResult[Set[InventoryStatus]]
// the abstract method that actually commit in backend repo the deletion from accepted nodes
- def commitDeleteAccepted(nodeInfo: NodeInfo, mode: DeleteMode, modId: ModificationId, actor: EventActor): IOResult[Unit]
+ def commitDeleteAccepted(nodeInfo: NodeInfo, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[Unit]
// the abstract method that actually commit in backend repo the deletion from accepted nodes
- def commitPurgeRemoved(nodeId: NodeId, mode: DeleteMode, modId: ModificationId, actor: EventActor): IOResult[Unit]
+ def commitPurgeRemoved(nodeId: NodeId, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[Unit]
+
+}
+
+class FactRemoveNodeBackend(backend: NodeFactRepository) extends RemoveNodeBackend {
+ override def findNodeStatuses(nodeId: NodeId): IOResult[Set[InventoryStatus]] = {
+ // here, we need to return "RemovedInventory" in case of missing node, so CoreNodeFactRepo #getStatus
+ // is not what we want;
+ backend.get(nodeId)(SelectNodeStatus.Any).map {
+ case None => Set(RemovedInventory)
+ case Some(x) => Set(x.rudderSettings.status)
+ }
+ }
+
+ override def commitDeleteAccepted(nodeInfo: NodeInfo, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[Unit] = {
+ backend.delete(nodeInfo.id).unit
+ }
+ override def commitPurgeRemoved(nodeId: NodeId, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[Unit] = {
+ backend.delete(nodeId).unit
+ }
}
class RemoveNodeServiceImpl(
@@ -184,7 +203,6 @@ class RemoveNodeServiceImpl(
nodeInfoService: NodeInfoService,
pathComputer: PathComputer,
newNodeManager: NewNodeManager,
- actionLogger: EventLogRepository,
val postNodeDeleteActions: Ref[List[PostNodeDeleteAction]],
HOOKS_D: String,
HOOKS_IGNORE_SUFFIXES: List[String]
@@ -203,11 +221,11 @@ class RemoveNodeServiceImpl(
* if so, copy the container to the removed inventory
* if not, move the container to the removed inventory
*
- * Return a couple with 2 boxes, one about the LDIF change, and one containing the result of the clear cache
+ * Return a couple RemoveNodeServiceImplwith 2 boxes, one about the LDIF change, and one containing the result of the clear cache
* The main goal is to separate the clear cache as it could fail while the node is correctly deleted.
* A failing clear cache should not be considered an error when deleting a Node.
*/
- override def removeNodePure(nodeId: NodeId, mode: DeleteMode, modId: ModificationId, actor: EventActor): IOResult[NodeInfo] = {
+ override def removeNodePure(nodeId: NodeId, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[NodeInfo] = {
// main logic, see help function below
nodeId match {
case Constants.ROOT_POLICY_SERVER_ID => Inconsistency("The root node cannot be deleted.").fail
@@ -221,7 +239,7 @@ class RemoveNodeServiceImpl(
res1 <- if (status.contains(PendingInventory)) {
(for {
i <- nodeInfoService.getPendingNodeInfo(nodeId)
- r <- deletePendingNode(nodeId, mode, modId, actor)
+ r <- deletePendingNode(nodeId, mode)
_ <- info.set(i)
} yield r).catchAll(err => Error(err).succeed)
} else Success.succeed
@@ -230,14 +248,14 @@ class RemoveNodeServiceImpl(
i <- nodeInfoService.getNodeInfo(nodeId)
r <- i match {
case None => Success.succeed // perhaps deleted or something
- case Some(x) => info.set(Some(x)) *> deleteAcceptedNode(x, mode, modId, actor)
+ case Some(x) => info.set(Some(x)) *> deleteAcceptedNode(x, mode)
}
} yield r).catchAll(err => Error(err).succeed)
} else Success.succeed
res3 <- if (status.contains(RemovedInventory)) {
(for {
i <- nodeInfoService.getDeletedNodeInfo(nodeId)
- r <- deleteDeletedNode(nodeId, mode, modId, actor)
+ r <- deleteDeletedNode(nodeId, mode)
// only update if nodeInfo is not already set, b/c accepted has more info
_ <- info.update(opt => opt.orElse(i))
} yield r).catchAll(err => Error(err).succeed)
@@ -248,7 +266,7 @@ class RemoveNodeServiceImpl(
_ <- NodeLoggerPure.Delete.debug(s"-> execute clean-up actions for node '${nodeId.value}'")
actions <- postNodeDeleteActions.get
optInfo <- info.get
- _ <- ZIO.foreachDiscard(actions)(_.run(nodeId, mode, optInfo, status, actor))
+ _ <- ZIO.foreachDiscard(actions)(_.run(nodeId, mode, optInfo, status))
_ <- NodeLoggerPure.Delete.info(
s"Node '${nodeId.value}' ${optInfo.map(_.hostname).getOrElse("")} was successfully deleted"
)
@@ -295,19 +313,14 @@ class RemoveNodeServiceImpl(
////////////////////////////////
// delete pending node is just refusing it
- def deletePendingNode(nodeId: NodeId, mode: DeleteMode, modId: ModificationId, actor: EventActor): IOResult[DeletionResult] = {
+ def deletePendingNode(nodeId: NodeId, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[DeletionResult] = {
NodeLoggerPure.Delete.debug(s"-> deleting node with ID '${nodeId.value}' from pending nodes (refuse)") *>
- newNodeManager.refuse(nodeId, modId, actor).toIO.map(_ => DeletionResult.Success)
+ newNodeManager.refuse(nodeId).map(_ => DeletionResult.Success)
}
// this is the core delete that is run on accepted node: pre hook, post hook, move to delete or erase
// in that case, we do have a nodeInfo
- def deleteAcceptedNode(
- nodeInfo: NodeInfo,
- mode: DeleteMode,
- modId: ModificationId,
- actor: EventActor
- ): IOResult[DeletionResult] = {
+ def deleteAcceptedNode(nodeInfo: NodeInfo, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[DeletionResult] = {
for {
_ <- NodeLoggerPure.Delete.debug(s"-> deleting node with ID '${nodeInfo.id.value}' from accepted nodes")
@@ -319,21 +332,10 @@ class RemoveNodeServiceImpl(
PreHookFailed(a).succeed
case _ =>
for {
- _ <- NodeLoggerPure.Delete.debug(s" - delete '${nodeInfo.id.value}' in LDAP (mode='${mode.name}')")
- _ <- backend.commitDeleteAccepted(nodeInfo, mode, modId, actor)
- invLogDetails = {
- InventoryLogDetails(
- nodeInfo.id,
- nodeInfo.inventoryDate,
- nodeInfo.hostname,
- nodeInfo.osDetails.fullName,
- actor.name
- )
- }
- eventlog = DeleteNodeEventLog.fromInventoryLogDetails(None, actor, invLogDetails)
- saved <- actionLogger.saveEventLog(modId, eventlog)
- _ <- NodeLoggerPure.Delete.debug(s" - run node post hooks for '${nodeInfo.id.value}'")
- postRun <- runPostHooks(hookEnv)
+ _ <- NodeLoggerPure.Delete.debug(s" - delete '${nodeInfo.id.value}' in LDAP (mode='${mode.name}')")
+ _ <- backend.commitDeleteAccepted(nodeInfo, mode)
+ _ <- NodeLoggerPure.Delete.debug(s" - run node post hooks for '${nodeInfo.id.value}'")
+ postRun <- runPostHooks(hookEnv)
} yield {
postRun match {
case stop: HookReturnCode.Error => PostHookFailed(stop)
@@ -345,13 +347,13 @@ class RemoveNodeServiceImpl(
}
// delete a node for which we only have the inventory, so it's either deleted, or in accepted but somehow broken.
- def deleteDeletedNode(nodeId: NodeId, mode: DeleteMode, modId: ModificationId, actor: EventActor): IOResult[DeletionResult] = {
+ def deleteDeletedNode(nodeId: NodeId, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[DeletionResult] = {
// if mode is move, done
if (mode == DeleteMode.MoveToRemoved) {
Success.succeed
} else { // erase
NodeLoggerPure.Delete.debug(s"-> erase '${nodeId.value}' from removed nodes") *>
- backend.commitPurgeRemoved(nodeId, mode, modId, actor).map(_ => Success)
+ backend.commitPurgeRemoved(nodeId, mode).map(_ => Success)
}
}
@@ -455,29 +457,19 @@ class LdapRemoveNodeBackend(
}
}
- override def commitPurgeRemoved(nodeId: NodeId, mode: DeleteMode, modId: ModificationId, actor: EventActor): IOResult[Unit] = {
+ override def commitPurgeRemoved(nodeId: NodeId, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[Unit] = {
fullNodeRepo.delete(nodeId, RemovedInventory).unit
}
// the part that just move/delete node
- override def commitDeleteAccepted(
- nodeInfo: NodeInfo,
- mode: DeleteMode,
- modId: ModificationId,
- actor: EventActor
- ): IOResult[Unit] = {
+ override def commitDeleteAccepted(nodeInfo: NodeInfo, mode: DeleteMode)(implicit cc: ChangeContext): IOResult[Unit] = {
for {
_ <-
- nodeLibMutex.writeLock(atomicDelete(nodeInfo.id, mode, modId, actor)).chainError("Error when deleting a node")
+ nodeLibMutex.writeLock(atomicDelete(nodeInfo.id, mode)).chainError("Error when deleting a node")
} yield ()
}
- def atomicDelete(
- nodeId: NodeId,
- mode: DeleteMode,
- modId: ModificationId,
- actor: EventActor
- ): IOResult[Seq[LDIFChangeRecord]] = {
+ def atomicDelete(nodeId: NodeId, mode: DeleteMode): IOResult[Seq[LDIFChangeRecord]] = {
for {
cleanNode <- deleteFromNodes(nodeId).chainError(s"Could not remove the node '${nodeId.value}' from base")
moveNodeInventory <- mode match {
@@ -515,14 +507,13 @@ class RemoveNodeFromGroups(
uuidGen: StringUuidGenerator
) extends PostNodeDeleteAction {
override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
+ nodeId: NodeId,
+ mode: DeleteMode,
+ info: Option[NodeInfo],
+ status: Set[InventoryStatus]
+ )(implicit cc: ChangeContext): UIO[Unit] = {
(for {
- _ <- NodeLoggerPure.Delete.debug(s" - remove node ${nodeId.value} from its groups")
+ _ <- NodeLoggerPure.Delete.debug(s" - remove node ${nodeId.value} from his groups")
nodeGroupIds <- roNodeGroupRepository.findGroupWithAnyMember(Seq(nodeId))
_ <- ZIO.foreach(nodeGroupIds) { nodeGroupId =>
val msg = Some("Automatic update of group due to deletion of node " + nodeId.value)
@@ -545,62 +536,17 @@ class RemoveNodeFromGroups(
}
}
-class RemoveNodeInfoFromCache(nodeInfoService: NodeInfoServiceCached) extends PostNodeDeleteAction {
- override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
- NodeLoggerPure.Delete.debug(s" - remove node from NodeInfoService Cache '${nodeId.value}'") *>
- nodeInfoService
- .removeNodeFromCache(nodeId)
- .catchAll(err => NodeLoggerPure.Delete.error(s"Error when removing node ${(nodeId, info).name} from cache: ${err.fullMsg}"))
- }
-}
-
-class RemoveNodeFromComplianceCache(
- configurationService: CachedNodeConfigurationService,
- cachedCompliance: CachedFindRuleNodeStatusReports
-) extends PostNodeDeleteAction {
- override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
- for {
- _ <- NodeLoggerPure.Delete.debug(s" - remove node ${nodeId.value} from compliance and expected report cache")
- _ <-
- configurationService
- .invalidateWithAction(Seq((nodeId, RemoveNodeInCache(nodeId))))
- .catchAll(err =>
- NodeLoggerPure.Delete.error(s"Error when removing node ${nodeId.value} from node configuration cache: ${err.fullMsg}")
- )
- _ <- cachedCompliance
- .invalidateWithAction(Seq((nodeId, ExpectedReportAction(RemoveNodeInCache(nodeId)))))
- .catchAll(err =>
- NodeLoggerPure.Delete.error(s"Error when removing node ${nodeId.value} from compliance cache: ${err.fullMsg}")
- )
- } yield {
- ()
- }
- }
-}
/*
* Close expected reports for node.
* Also delete nodes_info for that node.
*/
class CloseNodeConfiguration(expectedReportsRepository: UpdateExpectedReportsRepository) extends PostNodeDeleteAction {
override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
+ nodeId: NodeId,
+ mode: DeleteMode,
+ info: Option[NodeInfo],
+ status: Set[InventoryStatus]
+ )(implicit cc: ChangeContext): UIO[Unit] = {
for {
_ <- NodeLoggerPure.Delete.debug(s" - close expected reports for '${nodeId.value}'")
_ <- expectedReportsRepository
@@ -615,12 +561,11 @@ class CloseNodeConfiguration(expectedReportsRepository: UpdateExpectedReportsRep
// when the node is a policy server, delete directive/rule/group related to it
class DeletePolicyServerPolicies(policyServerManagement: PolicyServerManagementService) extends PostNodeDeleteAction {
override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
+ nodeId: NodeId,
+ mode: DeleteMode,
+ info: Option[NodeInfo],
+ status: Set[InventoryStatus]
+ )(implicit cc: ChangeContext): UIO[Unit] = {
// we can avoid to do LDAP requests if we are sure the node wasn't a policy server
info.map(_.isPolicyServer) match {
case Some(false) =>
@@ -641,12 +586,11 @@ class DeletePolicyServerPolicies(policyServerManagement: PolicyServerManagementS
// clean up certification key status (only in move mode, not erase)
class ResetKeyStatus(ldap: LDAPConnectionProvider[RwLDAPConnection], deletedDit: InventoryDit) extends PostNodeDeleteAction {
override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
+ nodeId: NodeId,
+ mode: DeleteMode,
+ info: Option[NodeInfo],
+ status: Set[InventoryStatus]
+ )(implicit cc: ChangeContext): UIO[Unit] = {
if (mode == DeleteMode.MoveToRemoved) {
NodeLoggerPure.Delete.debug(s" - reset node key certification status for '${nodeId.value}'") *>
(for {
@@ -667,12 +611,11 @@ class ResetKeyStatus(ldap: LDAPConnectionProvider[RwLDAPConnection], deletedDit:
// clean-up cfengine key - only possible if we still have an inventory
class CleanUpCFKeys extends PostNodeDeleteAction {
override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
+ nodeId: NodeId,
+ mode: DeleteMode,
+ info: Option[NodeInfo],
+ status: Set[InventoryStatus]
+ )(implicit cc: ChangeContext): UIO[Unit] = {
info match {
case Some(i) =>
val agentTypes = i.agentsName.map(_.agentType).toSet
@@ -732,12 +675,11 @@ class CleanUpNodePolicyFiles(varRudderShare: String) extends PostNodeDeleteActio
import better.files.File._
override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
+ nodeId: NodeId,
+ mode: DeleteMode,
+ info: Option[NodeInfo],
+ status: Set[InventoryStatus]
+ )(implicit cc: ChangeContext): UIO[Unit] = {
NodeLoggerPure.Delete.debug(s" - clean-up node '${nodeId.value}' policy files in /var/rudder/share") *>
cleanPoliciesRec(nodeId, File(varRudderShare)).runDrain.catchAll(err => {
NodeLoggerPure.Delete.info(
@@ -810,43 +752,3 @@ class CleanUpNodePolicyFiles(varRudderShare: String) extends PostNodeDeleteActio
})
}
}
-
-class DeleteNodeFact(nodeFactStorage: NodeFactStorage) extends PostNodeDeleteAction {
- override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
- NodeLoggerPure.Delete.debug(s" - delete fact about node '${nodeId.value}'") *>
- nodeFactStorage
- .changeStatus(nodeId, RemovedInventory)
- .catchAll(err =>
- NodeLoggerPure.info(s"Error when trying to update fact when deleting node '${nodeId.value}': ${err.fullMsg}")
- )
- .unit
- }
-}
-
-/*
- * This hook registers the deletion events into postgresql `nodefacts` table so that the inventory accept/refuse
- * fact can be latter cleaned-up.
- */
-class StoreDeleteEventHistory(history: InventoryHistoryJdbcRepository, cleanUpImmediately: Boolean) extends PostNodeDeleteAction {
- override def run(
- nodeId: NodeId,
- mode: DeleteMode,
- info: Option[NodeInfo],
- status: Set[InventoryStatus],
- actor: EventActor
- ): UIO[Unit] = {
- (if (cleanUpImmediately) {
- history.delete(nodeId)
- } else { // save delete event, clean-up will be automatically done by script
- history.saveDeleteEvent(nodeId, DateTime.now(), actor)
- }).catchAll(err =>
- NodeLoggerPure.warn(s"Error when updating node '${nodeId.value}' historical inventory information in base: ${err.fullMsg}")
- )
- }
-}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/HistoryLogRepository.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/HistoryLogRepository.scala
index 8355834ffe9..8497f541b56 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/HistoryLogRepository.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/HistoryLogRepository.scala
@@ -31,7 +31,7 @@ trait WriteOnlyHistoryLogRepository[ID, V, T, HLog <: HistoryLog[ID, V, T]] {
* @param historyLog
* @return
*/
- def save(id: ID, data: T, datetime: DateTime = DateTime.now): IOResult[HLog]
+ def save(id: ID, data: T, datetime: DateTime): IOResult[HLog]
}
@@ -50,7 +50,7 @@ trait ReadOnlyHistoryLogRepository[ID, V, T, HLog <: HistoryLog[ID, V, T]] {
* recorded history
* Full(hlog) the recorded version of hlog
*/
- def get(id: ID, version: V): IOResult[HLog]
+ def get(id: ID, version: V): IOResult[Option[HLog]]
/**
* Return the list of version for ID.
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/impl/FileHistoryLogRepository.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/impl/FileHistoryLogRepository.scala
index 4c4643f7812..38e2bae2129 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/impl/FileHistoryLogRepository.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/impl/FileHistoryLogRepository.scala
@@ -151,15 +151,14 @@ class FileHistoryLogRepository[ID: ClassTag, T](
}
/**
- * Get the list of record for the given UUID and version.
- * If no version is specified, get the last.
+ * Get the record for the given UUID and version if exists
*/
- def get(id: ID, version: DateTime): IOResult[HLog] = {
+ def get(id: ID, version: DateTime): IOResult[Option[HLog]] = {
for {
i <- idDir(id)
file <- ZIO.succeed(new File(i, vToS(version)))
- data <- parser.fromFile(file)
- } yield DefaultHLog(id, version, data)
+ data <- ZIO.whenZIO(IOResult.attempt(file.exists()))(parser.fromFile(file))
+ } yield data.map(d => DefaultHLog(id, version, d))
}
// we don't want to catch exception here
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/impl/InventoryHistoryJdbcRepository.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/impl/InventoryHistoryJdbcRepository.scala
index a0d8c3536d2..5a3199a600e 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/impl/InventoryHistoryJdbcRepository.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/nodes/history/impl/InventoryHistoryJdbcRepository.scala
@@ -118,7 +118,7 @@ class InventoryHistoryJdbcRepository(
* Save an inventory and return the ID of the saved inventory, and
* its version
*/
- override def save(id: NodeId, data: FactLogData, datetime: DateTime = DateTime.now): IOResult[FactLog] = {
+ override def save(id: NodeId, data: FactLogData, datetime: DateTime): IOResult[FactLog] = {
val event = NodeAcceptRefuseEvent(datetime, data.actor.name, data.status.name)
val q = sql"""insert into nodefacts (nodeId, acceptRefuseEvent, acceptRefuseFact) values (${id}, ${event}, ${data.fact})
on conflict (nodeId) do update set (acceptRefuseEvent, acceptRefuseFact) = (EXCLUDED.acceptRefuseEvent, EXCLUDED.acceptRefuseFact)"""
@@ -141,13 +141,13 @@ class InventoryHistoryJdbcRepository(
/**
* Get the record for the given UUID and version.
*/
- override def get(id: NodeId, version: DateTime): IOResult[FactLog] = {
+ override def get(id: NodeId, version: DateTime): IOResult[Option[FactLog]] = {
val q = {
sql"select nodeId, acceptRefuseEvent, acceptRefuseFact from nodefacts where nodeId = ${id.value} and acceptRefuseEvent ->>'date' = ${DateFormaterService
.serialize(version)}"
}
- transactIOResult(s"error when getting node '${id.value}' accept/refuse fact")(xa => q.query[FactLog].unique.transact(xa))
+ transactIOResult(s"error when getting node '${id.value}' accept/refuse fact")(xa => q.query[FactLog].option.transact(xa))
}
/**
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/policies/DeploymentService.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/policies/DeploymentService.scala
index 7190bb444c4..34d9198f958 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/policies/DeploymentService.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/policies/DeploymentService.scala
@@ -45,11 +45,9 @@ import com.normation.cfclerk.domain.ReportingLogic.FocusReport
import com.normation.cfclerk.domain.SectionSpec
import com.normation.cfclerk.domain.TechniqueName
import com.normation.errors._
-import com.normation.inventory.domain.AcceptedInventory
import com.normation.inventory.domain.AixOS
import com.normation.inventory.domain.MemorySize
import com.normation.inventory.domain.NodeId
-import com.normation.inventory.domain.NodeInventory
import com.normation.inventory.services.core.ReadOnlyFullInventoryRepository
import com.normation.rudder.batch.UpdateDynamicGroups
import com.normation.rudder.configuration.ConfigurationRepository
@@ -562,7 +560,6 @@ trait PromiseGenerationService {
def getDirectiveLibrary(ids: Set[DirectiveId]): Box[FullActiveTechniqueCategory]
def getGroupLibrary(): Box[FullNodeGroupCategory]
def getAllGlobalParameters: Box[Seq[GlobalParameter]]
- def getAllInventories(): Box[Map[NodeId, NodeInventory]]
def getGlobalComplianceMode(): Box[GlobalComplianceMode]
def getGlobalAgentRun(): Box[AgentRunInterval]
def getScriptEngineEnabled: () => Box[FeatureSwitch]
@@ -908,8 +905,6 @@ trait PromiseGeneration_performeIO extends PromiseGenerationService {
}
override def getGroupLibrary(): Box[FullNodeGroupCategory] = roNodeGroupRepository.getFullGroupLibrary().toBox
override def getAllGlobalParameters: Box[Seq[GlobalParameter]] = parameterService.getAllGlobalParameters()
- override def getAllInventories(): Box[Map[NodeId, NodeInventory]] =
- roInventoryRepository.getAllNodeInventories(AcceptedInventory).toBox
override def getGlobalComplianceMode(): Box[GlobalComplianceMode] = complianceModeService.getGlobalComplianceMode
override def getGlobalAgentRun(): Box[AgentRunInterval] = agentRunService.getGlobalAgentRun()
override def getAppliedRuleIds(
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/queries/LdapQueryProcessor.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/queries/LdapQueryProcessor.scala
index efb97c8e27b..9bfa5ad6d49 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/queries/LdapQueryProcessor.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/queries/LdapQueryProcessor.scala
@@ -38,7 +38,6 @@ package com.normation.rudder.services.queries
import cats.implicits._
import com.normation.NamedZioLogger
-import com.normation.box._
import com.normation.errors._
import com.normation.errors.RudderError
import com.normation.inventory.domain._
@@ -53,13 +52,10 @@ import com.normation.rudder.domain.nodes.NodeInfo
import com.normation.rudder.domain.queries._
import com.normation.rudder.repository.ldap.LDAPEntityMapper
import com.normation.rudder.services.nodes.NodeInfoService
-import com.normation.rudder.services.nodes.NodeInfoServiceCached
import com.normation.zio.currentTimeMillis
import com.unboundid.ldap.sdk.{LDAPConnection => _, SearchScope => _, _}
import com.unboundid.ldap.sdk.DereferencePolicy.NEVER
import java.util.regex.Pattern
-import net.liftweb.common._
-import net.liftweb.util.Helpers
import org.slf4j.LoggerFactory
import zio.{System => _, _}
import zio.syntax._
@@ -134,79 +130,6 @@ object InternalLDAPQueryProcessorLoggerPure extends NamedZioLogger {
override def loggerName: String = "com.normation.rudder.services.queries.InternalLDAPQueryProcessor"
}
-/**
- * Processor that translates Queries into LDAP search operations
- * for accepted nodes (it also checks that the node is registered
- * in the ou=Nodes branch)
- */
-class AcceptedNodesLDAPQueryProcessor(
- nodeDit: NodeDit,
- inventoryDit: InventoryDit,
- processor: InternalLDAPQueryProcessor,
- nodeInfoService: NodeInfoServiceCached
-) extends QueryProcessor with Loggable {
-
- private[this] case class QueryResult(
- nodeEntry: LDAPEntry,
- inventoryEntry: LDAPEntry,
- machineInfo: Option[LDAPEntry]
- )
-
- /**
- * only report entries that match query in also in node
- * @param query
- * @param select
- * @param limitToNodeIds
- * @return
- */
- private[this] def queryAndChekNodeId(
- query: Query,
- select: Seq[String],
- limitToNodeIds: Option[Seq[NodeId]]
- ): Box[Seq[NodeId]] = {
-
- val debugId = if (logger.isDebugEnabled) Helpers.nextNum else 0L
- val timePreCompute = System.currentTimeMillis
-
- for {
- foundNodes <-
- processor.internalQueryProcessor(query, select, limitToNodeIds, debugId, () => nodeInfoService.getAllNodeInfos()).toBox
- timeres = (System.currentTimeMillis - timePreCompute)
- _ = logger.debug(s"LDAP result: ${foundNodes.size} entries obtained in ${timeres}ms for query ${query.toString}")
- } yield {
- // filter out Rudder server component if necessary
- query.returnType match {
- case NodeReturnType =>
- // we have a special case for the root node that always never to that group, even if some weird
- // scenario lead to the removal (or non addition) of them
- val withoutServerRole = foundNodes.filterNot(_.value == "root")
- if (logger.isDebugEnabled) {
- val filtered = foundNodes.filter(_.value == "root")
- if (!filtered.isEmpty) {
- logger.debug(
- "[%s] [post-filter:policyServer] %s results: %s".format(debugId, withoutServerRole.size, filtered.mkString(", "))
- )
- }
- }
- withoutServerRole
- case NodeAndRootServerReturnType => foundNodes
- }
- }
- }
-
- override def process(query: Query): Box[Seq[NodeId]] = {
-
- // only keep the one of the form Full(...)
- queryAndChekNodeId(query, Seq(A_NODE_UUID), None)
- }
-
- override def processOnlyId(query: Query): Box[Seq[NodeId]] = {
- // only keep the one of the form Full(...)
- queryAndChekNodeId(query, Seq(A_NODE_UUID), None)
- }
-
-}
-
/**
* This is the last step of query, where we are looking for check in NodeInfo - and complex,
* the json path and check on node properties.
@@ -391,13 +314,138 @@ class InternalLDAPQueryProcessor(
* We have to make separated requests for special filter,
* and we need to have one by filter. So these one are in separated requests.
*
+ * => getMapDn
*/
- def ldapObjectTypeSets(normalizedQuery: LDAPNodeQuery) = createLDAPObjects(normalizedQuery, debugId)
+ for {
+ // log start query
+ _ <- logPure.debug(s"[${debugId}] Start search for ${query.toString}")
+ timeStart <- currentTimeMillis
+ // Construct & normalize the data
+ nq <- normalizedQuery
+ // special case: no query, but we create a dummy one,
+ // identified by noFilterButTakeAllFromCache = true
+ // in this case, we skip all the ldap part
+ optdms <- if (nq.noFilterButTakeAllFromCache) {
+ None.succeed
+ } else {
+ getMapDn(nq, debugId)
+ }
+
+ // Fetching all node infos if necessary
+ // This is an optimisation procedue, as it is a bit costly to fetch it, so we want to
+ // have it only if the query is an OR, and Invertion, or and AND and there ae
+ // no LDAP criteria
+ allNodesInfos <- query.composition match {
+ case Or => lambdaAllNodeInfos()
+ case And if nq.noFilterButTakeAllFromCache => lambdaAllNodeInfos()
+ case And if query.transform == ResultTransformation.Invert => lambdaAllNodeInfos()
+ case And if optdms.isDefined => lambdaAllNodeInfos()
+
+ case _ => Seq[NodeInfo]().succeed
+ }
+ timefetch <- currentTimeMillis
+ _ <-
+ logPure.debug(
+ s"[${debugId}] LDAP result: fetching if necessary all nodesInfos (${allNodesInfos.size} entries) in nodes obtained in ${timefetch - timeStart} ms for query ${query.toString}."
+ )
+
+ // If dnMapSets returns a None, then it means that we are ANDing composition with an empty value
+ // so we skip the last query
+ // It needs to returns a Seq because a Set of NodeInfo is really expensive to compute
+ results <- optdms match {
+ case None if nq.noFilterButTakeAllFromCache =>
+ allNodesInfos.succeed
+ case None =>
+ Seq[NodeInfo]().succeed
+ case Some(dms) =>
+ for {
+ ids <- executeLdapQueries(dms, nq, select, debugId)
+ _ <- logPure.trace(
+ s"[${debugId}] Found ${ids.size} entries ; filtering with ${allNodesInfos.size} accepted nodes"
+ )
+ } yield {
+ allNodesInfos.filter(nodeInfo => ids.contains(nodeInfo.node.id))
+ }
+ }
+ // No more LDAP query is required here
+ // Do the filtering about non LDAP data here
+ timeldap <- currentTimeMillis
+ _ <-
+ logPure.debug(
+ s"[${debugId}] LDAP result: ${results.size} entries in nodes obtained in ${timeldap - timeStart} ms for query ${query.toString}"
+ )
+
+ nodeIdFiltered = query.composition match {
+ case And if results.isEmpty =>
+ // And and nothing returns nothing
+ Seq[NodeId]()
+ case And =>
+ // If i'm doing and AND, there is no need for the allNodes here
+ PostFilterNodeFromInfoService.getLDAPNodeInfo(results, nq.nodeInfoFilters, query.composition, Seq())
+ case Or =>
+ // Here we need the list of all nodes
+ PostFilterNodeFromInfoService.getLDAPNodeInfo(
+ results,
+ nq.nodeInfoFilters,
+ query.composition,
+ allNodesInfos
+ )
+ }
+ timefilter <- currentTimeMillis
+ _ <-
+ logPure.debug(
+ s"[post-filter:rudderNode] Found ${nodeIdFiltered.size} nodes when filtering for info service existence and properties (${timefilter - timeldap} ms)"
+ )
+ _ <- logPure.ifDebugEnabled {
+ val filtered = results.map(x => x.node.id.value).diff(nodeIdFiltered.map(x => x.value))
+ if (filtered.nonEmpty) {
+ logPure.debug(
+ s"[${debugId}] [post-filter:rudderNode] ${nodeIdFiltered.size} results (following nodes not in ou=Nodes,cn=rudder-configuration or not matching filters on NodeInfo: ${filtered
+ .mkString(", ")}"
+ )
+ } else {
+ logPure.debug(
+ s"[${debugId}] [post-filter:rudderNode] ${nodeIdFiltered.size} results (following nodes not in ou=Nodes,cn=rudder-configuration or not matching filters on NodeInfo: ${filtered
+ .mkString(", ")}"
+ )
+
+ }
+ }
+
+ inverted = query.transform match {
+ case ResultTransformation.Identity => nodeIdFiltered
+ case ResultTransformation.Invert =>
+ logPure.logEffect.debug(s"[${debugId}] |- (need to get all nodeIds for inversion) ")
+ val res = allNodesInfos.map(_.id).diff(nodeIdFiltered)
+ logPure.logEffect.debug(s"[${debugId}] |- (invert) entries after inversion: ${res.size}")
+ res
+ }
+ postFiltered = postFilterNode(inverted, query.returnType, limitToNodeIds)
+ } yield {
+ postFiltered
+ }
+ }
+
+ /*
+ * A raw execution without log, special optimisation case, invert, etc
+ */
+ def rawInternalQueryProcessor(query: Query, debugId: Long = 0L): IOResult[Seq[NodeId]] = {
+ for {
+ nq <- normalize(query).toIO.chainError("Error when normalizing LDAP query")
+ ids <- getMapDn(nq, debugId).flatMap {
+ case None => Seq().succeed
+ case Some(dns) => executeLdapQueries(dns, nq, Seq("1.1"), debugId)
+ }
+ } yield ids
+ }
+
+ def getMapDn(nq: LDAPNodeQuery, debugId: Long): IOResult[Option[Map[DnType, Set[DN]]]] = {
// then, actually execute queries
def dnMapMapSets(
normalizedQuery: LDAPNodeQuery,
- ldapObjectTypeSets: Map[DnType, Map[String, LDAPObjectType]]
+ ldapObjectTypeSets: Map[DnType, Map[String, LDAPObjectType]],
+ debugId: Long
): IOResult[Map[DnType, Map[String, Set[DN]]]] = {
ZIO
.foreach(ldapObjectTypeSets) {
@@ -465,14 +513,30 @@ class InternalLDAPQueryProcessor(
}
}
+ for {
+ lots <- createLDAPObjects(nq, debugId)
+ dmms <- dnMapMapSets(nq, lots, debugId)
+ } yield {
+ dnMapSets(nq, dmms)
+ }
+ }
+
+ def executeLdapQueries(
+ dms: Map[DnType, Set[DN]],
+ nq: LDAPNodeQuery,
+ select: Seq[String],
+ debugId: Long
+ ): IOResult[Seq[NodeId]] = {
// transform all the DNs we get to filters for the targeted object type
// here, we are objectType dependent: we use a mapping that is saying
// "for that objectType, that dnType is transformed into a filter like that"
def filterSeqSet(dnMapSets: Map[DnType, Set[DN]]): Seq[Set[Filter]] = {
- (dnMapSets map {
- case (dnType, dnMapSet) =>
- dnMapSet map { dn => nodeJoinFilters(dnType)(dn) }
- }).toSeq
+ (
+ dnMapSets map {
+ case (dnType, dnMapSet) =>
+ dnMapSet map { dn => nodeJoinFilters(dnType)(dn) }
+ }
+ ).toSeq
}
// now, build last filter depending on comparator :
@@ -515,148 +579,39 @@ class InternalLDAPQueryProcessor(
}
}
- for {
- // log start query
- _ <- logPure.debug(s"[${debugId}] Start search for ${query.toString}")
- timeStart <- currentTimeMillis
- // Construct & normalize the data
- nq <- normalizedQuery
- // special case: no query, but we create a dummy one,
- // identified by noFilterButTakeAllFromCache = true
- // in this case, we skip all the ldap part
- optdms <- {
- if (nq.noFilterButTakeAllFromCache) {
- None.succeed
- } else {
-
- for {
- lots <- ldapObjectTypeSets(nq)
- dmms <- dnMapMapSets(nq, lots)
- inneroptdms = dnMapSets(nq, dmms)
- } yield {
- inneroptdms
- }
- }
- }
-
- // Fetching all node infos if necessary
- // This is an optimisation procedue, as it is a bit costly to fetch it, so we want to
- // have it only if the query is an OR, and Invertion, or and AND and there ae
- // no LDAP criteria
- allNodesInfos <- query.composition match {
- case Or => lambdaAllNodeInfos()
- case And if nq.noFilterButTakeAllFromCache => lambdaAllNodeInfos()
- case And if query.transform == ResultTransformation.Invert => lambdaAllNodeInfos()
- case And if optdms.isDefined => lambdaAllNodeInfos()
-
- case _ => Seq[NodeInfo]().succeed
- }
- timefetch <- currentTimeMillis
- _ <-
- logPure.debug(
- s"LDAP result: fetching if necessary all nodesInfos (${allNodesInfos.size} entries) in nodes obtained in ${timefetch - timeStart} ms for query ${query.toString}."
- )
-
- // If dnMapSets returns a None, then it means that we are ANDing composition with an empty value
- // so we skip the last query
- // It needs to returns a Seq because a Set of NodeInfo is really expensive to compute
- results <- optdms match {
- case None if nq.noFilterButTakeAllFromCache =>
- allNodesInfos.succeed
- case None =>
- Seq[NodeInfo]().succeed
- case Some(dms) =>
- (for {
- // Ok, do the computation here
- // still rely on LDAP here
- _ <- logPure.ifTraceEnabled {
- ZIO.foreachDiscard(dms) {
- case (dnType, dns) =>
- logPure.trace(s"/// ${dnType} ==> ${dns.map(_.getRDN).mkString(", ")}")
- }
- }
- fss = filterSeqSet(dms)
- blf <- buildLastFilter(nq, fss)
-
- // for convenience
- (finalLdapFilter, finalSpecialFilters) = blf
-
- // final query, add "match only server id" filter if needed
- rt = nodeObjectTypes.copy(filter = finalLdapFilter)
- _ <- logPure.debug(s"[${debugId}] |- (final query) ${rt}")
- entries <- executeQuery(
- rt.baseDn,
- rt.scope,
- nodeObjectTypes.objectFilter,
- rt.filter,
- finalSpecialFilters,
- select.toSet,
- nq.composition,
- debugId
- )
- // convert these entries into nodeInfo
- nodesId = entries.flatMap(x => x(A_NODE_UUID)).toSet
- nodeInfos = allNodesInfos.filter(nodeInfo => nodesId.contains(nodeInfo.node.id.value))
- } yield nodeInfos)
- .tapError(err => logPure.debug(s"[${debugId}] `-> error: ${err.fullMsg}"))
- .tap(seq => logPure.debug(s"[${debugId}] `-> ${seq.size} results"))
- }
- // No more LDAP query is required here
- // Do the filtering about non LDAP data here
- timeldap <- currentTimeMillis
- _ <- logPure.debug(
- s"LDAP result: ${results.size} entries in nodes obtained in ${timeldap - timeStart} ms for query ${query.toString}"
- )
-
- nodeIdFiltered = query.composition match {
- case And if results.isEmpty =>
- // And and nothing returns nothing
- Seq[NodeId]()
- case And =>
- // If i'm doing and AND, there is no need for the allNodes here
- PostFilterNodeFromInfoService.getLDAPNodeInfo(results, nq.nodeInfoFilters, query.composition, Seq())
- case Or =>
- // Here we need the list of all nodes
- PostFilterNodeFromInfoService.getLDAPNodeInfo(
- results,
- nq.nodeInfoFilters,
- query.composition,
- allNodesInfos
- )
- }
- timefilter <- currentTimeMillis
- _ <-
- logPure.debug(
- s"[post-filter:rudderNode] Found ${nodeIdFiltered.size} nodes when filtering for info service existence and properties (${timefilter - timeldap} ms)"
- )
- _ <- logPure.ifDebugEnabled {
- val filtered = results.map(x => x.node.id.value).diff(nodeIdFiltered.map(x => x.value))
- if (filtered.nonEmpty) {
- logPure.debug(
- s"[${debugId}] [post-filter:rudderNode] ${nodeIdFiltered.size} results (following nodes not in ou=Nodes,cn=rudder-configuration or not matching filters on NodeInfo: ${filtered
- .mkString(", ")}"
- )
- } else {
- logPure.debug(
- s"[${debugId}] [post-filter:rudderNode] ${nodeIdFiltered.size} results (following nodes not in ou=Nodes,cn=rudder-configuration or not matching filters on NodeInfo: ${filtered
- .mkString(", ")}"
- )
-
- }
- }
-
- inverted = query.transform match {
- case ResultTransformation.Identity => nodeIdFiltered
- case ResultTransformation.Invert =>
- logPure.logEffect.debug(s"[${debugId}] |- (need to get all nodeIds for inversion) ")
- val res = allNodesInfos.map(_.id).diff(nodeIdFiltered)
- logPure.logEffect.debug(s"[${debugId}] |- (invert) entries after inversion: ${res.size}")
- res
- }
- postFiltered = postFilterNode(inverted, query.returnType, limitToNodeIds)
- } yield {
- postFiltered
- }
+ (
+ for {
+ // Ok, do the computation here
+ // still rely on LDAP here
+ _ <- logPure.ifTraceEnabled {
+ ZIO.foreachDiscard(dms) {
+ case (dnType, dns) =>
+ logPure.trace(s"/// ${dnType} ==> ${dns.map(_.getRDN).mkString(", ")}")
+ }
+ }
+ fss = filterSeqSet(dms)
+ blf <- buildLastFilter(nq, fss)
+
+ // for convenience
+ (finalLdapFilter, finalSpecialFilters) = blf
+
+ // final query, add "match only server id" filter if needed
+ rt = nodeObjectTypes.copy(filter = finalLdapFilter)
+ _ <- logPure.debug(s"[${debugId}] |- (final query) ${rt}")
+ entries <- executeQuery(
+ rt.baseDn,
+ rt.scope,
+ nodeObjectTypes.objectFilter,
+ rt.filter,
+ finalSpecialFilters,
+ select.toSet,
+ nq.composition,
+ debugId
+ )
+ } yield entries.flatMap(x => x(A_NODE_UUID).map(NodeId(_)))
+ )
+ .tapError(err => logPure.debug(s"[${debugId}] `-> error: ${err.fullMsg}"))
+ .tap(seq => logPure.debug(s"[${debugId}] `-> ${seq.size} results"))
}
/**
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/queries/NodeFactQueryProcessor.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/queries/NodeFactQueryProcessor.scala
new file mode 100644
index 00000000000..f6275ab5c70
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/queries/NodeFactQueryProcessor.scala
@@ -0,0 +1,248 @@
+/*
+ *************************************************************************************
+ * Copyright 2023 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.services.queries
+
+import com.normation.box._
+import com.normation.errors.IOResult
+import com.normation.inventory.domain.AcceptedInventory
+import com.normation.inventory.domain.InventoryStatus
+import com.normation.inventory.domain.NodeId
+import com.normation.inventory.domain.PendingInventory
+import com.normation.inventory.domain.RemovedInventory
+import com.normation.rudder.domain.logger.FactQueryProcessorLoggerPure
+import com.normation.rudder.domain.nodes.NodeGroupId
+import com.normation.rudder.domain.nodes.NodeGroupUid
+import com.normation.rudder.domain.nodes.NodeKind
+import com.normation.rudder.domain.queries._
+import com.normation.rudder.facts.nodes.CoreNodeFact
+import com.normation.rudder.facts.nodes.NodeFactRepository
+import com.normation.rudder.facts.nodes.SelectNodeStatus
+import com.normation.zio._
+import net.liftweb.common.Box
+import zio._
+import zio.stream.ZSink
+import zio.syntax._
+
+/*
+ * A NodeFactMatcher is the transformation of a query into a method that is able to
+ * eval a NodeFact for that Query.
+ * It takes into account:
+ * - the different case (node, root, invert, etc)
+ * - the query criteria
+ *
+ * NodeFactMatcher is a group for AND and for OR
+ */
+final case class NodeFactMatcher(debugString: String, matches: CoreNodeFact => IOResult[Boolean])
+
+object NodeFactMatcher {
+ val nodeAndRelayMatcher = {
+ val s = "only matches node and relay"
+ NodeFactMatcher(
+ s,
+ (n: CoreNodeFact) => {
+ for {
+ res <- (n.rudderSettings.kind != NodeKind.Root).succeed
+ _ <- FactQueryProcessorLoggerPure.trace(s" [${res}] for $s on '${n.rudderSettings.kind}'")
+ } yield res
+ }
+ )
+ }
+}
+
+trait Group {
+ def compose(a: NodeFactMatcher, b: NodeFactMatcher): NodeFactMatcher
+ def inverse(a: NodeFactMatcher): NodeFactMatcher
+ def zero: NodeFactMatcher
+}
+object GroupAnd extends Group {
+ def compose(a: NodeFactMatcher, b: NodeFactMatcher) = {
+ (a, b) match {
+ case (`zero`, _) => b
+ case (_, `zero`) => a
+ case _ => NodeFactMatcher(s"(${a.debugString}) && (${b.debugString})", (n: CoreNodeFact) => (a.matches(n) && b.matches(n)))
+ }
+ }
+ def inverse(a: NodeFactMatcher) = NodeFactMatcher(s"!(${a.debugString})", (n: CoreNodeFact) => a.matches(n).map(!_))
+ val zero = NodeFactMatcher("true", _ => true.succeed)
+}
+
+object GroupOr extends Group {
+ def compose(a: NodeFactMatcher, b: NodeFactMatcher) = {
+ (a, b) match {
+ case (`zero`, _) => b
+ case (_, `zero`) => a
+ case _ => NodeFactMatcher(s"(${a.debugString}) || (${b.debugString})", (n: CoreNodeFact) => (a.matches(n) || b.matches(n)))
+ }
+ }
+ def inverse(a: NodeFactMatcher) = NodeFactMatcher(s"!(${a.debugString})", (n: CoreNodeFact) => a.matches(n).map(!_))
+ val zero = NodeFactMatcher("false", _ => false.succeed)
+}
+
+class NodeFactQueryProcessor(
+ nodeFactRepo: NodeFactRepository,
+ groupRepo: SubGroupComparatorRepository,
+ ldapQueryProc: InternalLDAPQueryProcessor,
+ status: InventoryStatus = AcceptedInventory
+) extends QueryProcessor with QueryChecker {
+
+ def process(query: Query): Box[Seq[NodeId]] = processPure(query).map(_.toList.map(_.id)).toBox
+ def processOnlyId(query: Query): Box[Seq[NodeId]] = processPure(query).map(_.toList.map(_.id)).toBox
+
+ def check(query: Query, nodeIds: Option[Seq[NodeId]]): IOResult[Set[NodeId]] = { ??? }
+ def processPure(query: Query): IOResult[Chunk[CoreNodeFact]] = {
+ def process(s: SelectNodeStatus) = {
+ for {
+ t0 <- currentTimeMillis
+ m <- analyzeQuery(query)
+ t1 <- currentTimeMillis
+ _ <- FactQueryProcessorLoggerPure.Metrics.debug(s"Analyse query in ${t1 - t0} ms")
+ res <- nodeFactRepo
+ .getAll()(s)
+ .filterZIO(node => FactQueryProcessorLoggerPure.debug(m.debugString) *> processOne(m, node))
+ .run(ZSink.collectAll)
+ t2 <- currentTimeMillis
+ _ <- FactQueryProcessorLoggerPure.Metrics.debug(s"Run query in ${t2 - t1} ms")
+ _ <- FactQueryProcessorLoggerPure.debug(s"Found ${res.size} results")
+ _ <- FactQueryProcessorLoggerPure.trace(s"Matching nodes: '${res.map(_.id.value).mkString("', '")}'")
+ } yield res
+ }
+
+ status match {
+ case AcceptedInventory => process(SelectNodeStatus.Accepted)
+ case PendingInventory => process(SelectNodeStatus.Pending)
+ case RemovedInventory => Chunk.empty.succeed
+ }
+ }
+
+ /*
+ * transform the query into a function to apply to a NodeFact and that say "yes" or "no"
+ *
+ * We have different "querier":
+ * - CoreNodeFact (we have info in cache)
+ * - SubGroupQuery (we want to do only one query to external service for each line)
+ * - LdapQuery (we want to have all lines of that kind grouped in a new query)
+ */
+ def analyzeQuery(query: Query): IOResult[NodeFactMatcher] = {
+ val group = if (query.composition == And) GroupAnd else GroupOr
+
+ // we need a better pattern matching (extensible would be better) in place of `isInstanceOf`
+ val subgroupLines = subGroupMatcher(
+ query.criteria.collect {
+ case l if l.attribute.cType.isInstanceOf[SubGroupComparator] => l
+ },
+ groupRepo
+ )
+ val ldapLines = ldapMatcher(
+ query.criteria.collect {
+ case l if l.attribute.cType.isInstanceOf[LDAPCriterionType] => l
+ },
+ query
+ )
+ val nodeLines = nodeFactMatcher(query.criteria.collect {
+ case l if l.attribute.nodeCriterionMatcher != UnsupportedByNodeMinimalApi => l
+ })
+
+ for {
+ // build matcher for criterion lines
+ lineResult <- ZIO.foldLeft(subgroupLines ++ ldapLines ++ nodeLines)(group.zero) {
+ case (matcher, line) =>
+ line.map(group.compose(matcher, _))
+ }
+ } yield {
+ // inverse now if needed, because we don't want to return root if not asked *even* when inverse is present
+ val inv = if (query.transform == ResultTransformation.Invert) group.inverse(lineResult) else lineResult
+ // finally, filter out root if need
+ val res =
+ if (query.returnType == NodeAndRootServerReturnType) inv else GroupAnd.compose(NodeFactMatcher.nodeAndRelayMatcher, inv)
+ res
+ }
+ }
+
+ // here, we have two kinds of processing:
+ // - one is testing on a node by node basis: this is the main case, where we want to know if a node
+ // matches of not a bunch of criteria regarding its inventory/properties
+ // - one is "on all node at once": it is when an external service is the oracle and can decide what node
+ // matches its criteria. This is the case for the node-group matcher for example.
+ // For now, we have to check the criterion to know, each external service is a special case
+ def ldapMatcher(lines: Seq[CriterionLine], q: Query): Seq[IOResult[NodeFactMatcher]] = {
+ // for LDAP, we rebuild a false query with only these lines and no transformation
+ val ldapQuery = q.copy(transform = ResultTransformation.Identity, criteria = lines.toList)
+ Seq(for {
+ nodeIds <- ldapQueryProc.rawInternalQueryProcessor(ldapQuery)
+ } yield {
+ NodeFactMatcher(
+ s"[sub-ldap query ${ldapQuery.toString()}]",
+ (n: CoreNodeFact) => nodeIds.contains(n.id).succeed
+ )
+ })
+ }
+
+ def nodeFactMatcher(lines: Seq[CriterionLine]): Seq[IOResult[NodeFactMatcher]] = {
+ lines.map { c =>
+ NodeFactMatcher(
+ s"[${c.objectType.objectType}.${c.attribute.name} ${c.comparator.id} ${c.value}]",
+ (n: CoreNodeFact) => c.attribute.nodeCriterionMatcher.matches(n, c.comparator, c.value)
+ ).succeed
+ }
+ }
+
+ def subGroupMatcher(
+ lines: Seq[CriterionLine],
+ groupRepo: SubGroupComparatorRepository
+ ): Seq[IOResult[NodeFactMatcher]] = {
+ lines.map { c =>
+ for {
+ groupNodes <- groupRepo.getNodeIds(NodeGroupId(NodeGroupUid(c.value)))
+ } yield {
+ NodeFactMatcher(
+ s"[${c.objectType.objectType}.${c.attribute.name} ${c.comparator.id} ${c.value}]",
+ (n: CoreNodeFact) => groupNodes.contains(n.id).succeed
+ )
+ }
+ }
+ }
+
+ def processOne(matcher: NodeFactMatcher, n: CoreNodeFact): IOResult[Boolean] = {
+ for {
+ _ <- FactQueryProcessorLoggerPure.debug(s" --'${n.fqdn}' (${n.id.value})--")
+ res <- matcher.matches(n)
+ _ <- FactQueryProcessorLoggerPure.debug(s" = [${res}] on '${n.fqdn}' (${n.id.value})")
+ } yield res
+ }
+
+}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchBackendImpl.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchBackendImpl.scala
index 948901eaf62..ffe35d4eb56 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchBackendImpl.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchBackendImpl.scala
@@ -38,7 +38,6 @@
package com.normation.rudder.services.quicksearch
import com.normation.box._
-import com.normation.inventory.ldap.core.InventoryDit
import com.normation.inventory.ldap.core.LDAPConstants._
import com.normation.ldap.sdk.BuildFilter._
import com.normation.ldap.sdk.LDAPBoolean
@@ -46,15 +45,17 @@ import com.normation.ldap.sdk.LDAPConnectionProvider
import com.normation.ldap.sdk.LDAPEntry
import com.normation.ldap.sdk.RoLDAPConnection
import com.normation.ldap.sdk.Sub
-import com.normation.rudder.domain.NodeDit
import com.normation.rudder.domain.RudderDit
import com.normation.rudder.domain.RudderLDAPConstants._
import com.normation.rudder.domain.policies.Tag
import com.normation.rudder.domain.policies.TagName
import com.normation.rudder.domain.policies.TagValue
+import com.normation.rudder.domain.properties.NodeProperty
+import com.normation.rudder.facts.nodes.CoreNodeFact
+import com.normation.rudder.facts.nodes.MinimalNodeFactInterface
+import com.normation.rudder.facts.nodes.NodeFactRepository
import com.normation.rudder.repository.RoDirectiveRepository
import com.normation.rudder.repository.json.DataExtractor.CompleteJson
-import com.normation.rudder.services.nodes.NodeInfoService
import com.unboundid.ldap.sdk.Attribute
import com.unboundid.ldap.sdk.Filter
import java.util.regex.Pattern
@@ -62,6 +63,7 @@ import net.liftweb.common.Box
import net.liftweb.common.Full
import net.liftweb.common.Loggable
import scala.util.control.NonFatal
+import zio.stream.ZSink
/**
* Correctly quote a token
@@ -72,15 +74,118 @@ object QSPattern {
}
/**
- * This file contains the differents possible implementation of
+ * This file contains the different possible implementations of
* quick search backends.
*
* Backend are able to transform a query in a set of
* quicksearch results.
*
- * For now, we have one for directive, and a different one
+ * For now, we have one for nodefacts, one for directive, and a different one
* of everything else.
*/
+object QSNodeFactBackend extends Loggable {
+ import QSAttribute.{NodeId => QSNodeId, _}
+ import QSObject.{Node => QSNode}
+ import QuickSearchResultId.QRNodeId
+
+ /*
+ * The filter that allows to know if a couple (activeTechnique, directive) match
+ * the expected query
+ */
+
+ /**
+ * Lookup directives
+ */
+ def search(query: Query)(implicit repo: NodeFactRepository): Box[Seq[QuickSearchResult]] = {
+
+ // only search if query is on Directives and attributes contains
+ // DirectiveId, DirectiveVarName, DirectiveVarValue, TechniqueName, TechniqueVersion
+
+ val attributes: Set[QSAttribute] = query.attributes.intersect(QSObject.Node.attributes)
+
+ if (query.objectClass.contains(QSNode) && attributes.nonEmpty) {
+
+ repo
+ .getAll()
+ .mapConcat((n: CoreNodeFact) => attributes.flatMap(a => a.find(n, query.userToken)))
+ .run(ZSink.collectAll)
+ .toBox
+ } else {
+ Full(Seq())
+ }
+ }
+
+ implicit class QSAttributeFilter(val a: QSAttribute) extends AnyVal {
+
+ private[this] def toMatch(node: CoreNodeFact): Option[Set[(String, String)]] = {
+
+ def someSet(v: String) = Some(Set((v, v)))
+ def optSet(v: Option[String]) = v.map(x => Set((x, x)))
+
+ /*
+ * A set of value to check against / value to return to the user
+ */
+ a match {
+ case QSNodeId => someSet(node.id.value)
+ case Fqdn => someSet(node.fqdn)
+ case OsType => someSet(node.os.os.kernelName)
+ case OsName => someSet(node.os.os.name)
+ case OsVersion => someSet(node.os.version.value)
+ case OsFullName => someSet(node.os.fullName)
+ case OsKernelVersion => someSet(node.os.kernelVersion.value)
+ case OsServicePack => optSet(node.os.servicePack)
+ case Arch => optSet(node.archDescription)
+ case Ram => optSet(node.ram.map(_.size.toString))
+ case IpAddresses => Some(MinimalNodeFactInterface.ipAddresses(node).map(ip => (ip, ip)).toSet)
+ case PolicyServerId => someSet(node.rudderSettings.policyServerId.value)
+ case Properties =>
+ Some(node.properties.collect {
+ case p if (p.provider != Some(NodeProperty.customPropertyProvider)) => (p.toData, p.toData)
+ }.toSet)
+ case GroupProperties => None
+ case CustomProperties =>
+ Some(node.properties.collect {
+ case p if (p.provider == Some(NodeProperty.customPropertyProvider)) => (p.toData, p.toData)
+ }.toSet)
+ case NodeState => someSet(node.rudderSettings.state.name)
+ case DirectiveId => None
+ case DirectiveVarName => None
+ case DirectiveVarValue => None
+ case TechniqueName => None
+ case TechniqueId => None
+ case TechniqueVersion => None
+ case Description => None
+ case LongDescription => None
+ case Name => None
+ case IsEnabled => None
+ case Tags => None
+ case TagKeys => None
+ case TagValues => None
+ case GroupId => None
+ case IsDynamic => None
+ case ParameterName => None
+ case ParameterValue => None
+ case RuleId => None
+ case DirectiveIds => None
+ case Targets => None
+ }
+ }
+
+ def find(node: CoreNodeFact, token: String): Option[QuickSearchResult] = {
+ toMatch(node).flatMap { set =>
+ set.collectFirst {
+ case (s, value) if QSPattern(token).matcher(s).matches =>
+ QuickSearchResult(
+ QRNodeId(node.id.value),
+ s"${node.fqdn} [${node.id.value}]",
+ Some(a),
+ value
+ )
+ }
+ }
+ }
+ }
+}
object QSDirectiveBackend extends Loggable {
import QSAttribute.{DirectiveId => QSDirectiveId, _}
@@ -97,7 +202,7 @@ object QSDirectiveBackend extends Loggable {
/**
* Lookup directives
*/
- def search(query: Query)(implicit repo: RoDirectiveRepository): Box[Set[QuickSearchResult]] = {
+ def search(query: Query)(implicit repo: RoDirectiveRepository): Box[Seq[QuickSearchResult]] = {
// only search if query is on Directives and attributes contains
// DirectiveId, DirectiveVarName, DirectiveVarValue, TechniqueName, TechniqueVersion
@@ -114,10 +219,10 @@ object QSDirectiveBackend extends Loggable {
attribute <- attributes
} yield {
attribute.find(at, dir, query.userToken)
- }).flatten.toSet
+ }).flatten.toSeq
}
} else {
- Full(Set())
+ Full(Seq())
}
}
@@ -197,8 +302,8 @@ object QSDirectiveBackend extends Loggable {
}
/**
- * The whole LDAP backend logic: look for Nodes, NodeGroups, Parameters, Rules,
- * but not Directives.
+ * The whole LDAP backend logic: look for NodeGroups, Parameters, Rules,
+ * but not Directives nor Nodes
*/
object QSLdapBackend {
import QSAttribute._
@@ -209,10 +314,7 @@ object QSLdapBackend {
*/
def search(query: Query)(implicit
ldap: LDAPConnectionProvider[RoLDAPConnection],
- inventoryDit: InventoryDit,
- nodeDit: NodeDit,
- rudderDit: RudderDit,
- nodeInfos: NodeInfoService
+ rudderDit: RudderDit
): Box[Seq[QuickSearchResult]] = {
// the filter for attribute and for attributes must be non empty, else return nothing
val ocFilter = query.objectClass.map(_.filter).flatten.toSeq
@@ -229,31 +331,16 @@ object QSLdapBackend {
for {
connection <- ldap
- nodeIds <- nodeInfos.getAllNodesIds()
- entries <- connection.search(nodeDit.BASE_DN, Sub, filter, returnedAttributes: _*)
+ entries <- connection.search(rudderDit.BASE_DN, Sub, filter, returnedAttributes: _*)
} yield {
if (ocFilter.isEmpty || attrFilter.isEmpty) { // nothing to search for in that backend
Seq()
} else {
-
- // here, we must merge "nodes" so that we don't report in log two times too many results,
- // and we get node always with a hostname
- val (nodes, others) = entries.partition(x => x.isA(OC_NODE) || x.isA(OC_RUDDER_NODE))
- // merge node attribute for node entries with same node id
- val merged = nodes.groupBy(_.value_!(A_NODE_UUID)).filter(e => nodeIds.map(_.value).contains(e._1)).map {
- case (_, samenodes) =>
- samenodes.reduce[LDAPEntry] {
- case (n1, n2) =>
- n2.attributes.foreach(a => n1 mergeAttribute a)
- n1
- }
- }
-
// transformat LDAPEntries to quicksearch results, keeping only the attribute
// that matches the query on the result and no system entries but nodes.
// Also, only keep nodes that exists.
- (others ++ merged).flatMap(_.toResult(query))
+ entries.flatMap(_.toResult(query))
}
}
}.toBox
@@ -267,24 +354,24 @@ object QSLdapBackend {
Description -> A_DESCRIPTION,
LongDescription -> A_LONG_DESCRIPTION,
IsEnabled -> A_IS_ENABLED,
- NodeId -> A_NODE_UUID,
- Fqdn -> A_HOSTNAME,
- OsType -> A_OC,
- OsName -> A_OS_NAME,
- OsVersion -> A_OS_VERSION,
- OsFullName -> A_OS_FULL_NAME,
- OsKernelVersion -> A_OS_KERNEL_VERSION,
- OsServicePack -> A_OS_SERVICE_PACK,
- Arch -> A_ARCH,
- Ram -> A_OS_RAM,
- IpAddresses -> A_LIST_OF_IP,
- PolicyServerId -> A_POLICY_SERVER_UUID,
- Properties -> A_NODE_PROPERTY,
- GroupProperties -> A_JSON_PROPERTY,
- CustomProperties -> A_CUSTOM_PROPERTY,
- NodeState -> A_STATE,
GroupId -> A_NODE_GROUP_UUID,
IsDynamic -> A_IS_DYNAMIC,
+ NodeId -> A_NODE_UUID,
+ Fqdn -> "",
+ OsType -> "",
+ OsName -> "",
+ OsVersion -> "",
+ OsFullName -> "",
+ OsKernelVersion -> "",
+ OsServicePack -> "",
+ Arch -> "",
+ Ram -> "",
+ IpAddresses -> "",
+ PolicyServerId -> "",
+ Properties -> "",
+ GroupProperties -> A_JSON_PROPERTY,
+ CustomProperties -> "",
+ NodeState -> "",
DirectiveId -> "",
DirectiveVarName -> "",
DirectiveVarValue -> "",
@@ -463,17 +550,12 @@ object QSLdapBackend {
* Build LDAP filter for a QSObject
*/
implicit final class QSObjectLDAPFilter(obj: QSObject)(implicit
- inventoryDit: InventoryDit,
- nodeDit: NodeDit,
rudderDit: RudderDit
) {
def filter = obj match {
case Common => Nil
- case Node => (
- AND(IS(OC_NODE), Filter.create(s"entryDN:dnOneLevelMatch:=${inventoryDit.NODES.dn.toString}"))
- :: AND(IS(OC_RUDDER_NODE), Filter.create(s"entryDN:dnOneLevelMatch:=${nodeDit.NODES.dn.toString}")) :: Nil
- )
+ case Node => Nil
case Group => AND(IS(OC_RUDDER_NODE_GROUP), Filter.create(s"entryDN:dnSubtreeMatch:=${rudderDit.GROUP.dn.toString}")) :: Nil
case Directive => Nil
case Parameter =>
@@ -485,15 +567,13 @@ object QSLdapBackend {
/**
* correctly transform entry to a result, putting what is needed in type and description
*/
- implicit final class EntryToSearchResult(val e: LDAPEntry)(implicit val nodeInfos: NodeInfoService) {
+ implicit final class EntryToSearchResult(val e: LDAPEntry) {
import QSAttributeLdapFilter._
import QuickSearchResultId._
def toResult(query: Query): Option[QuickSearchResult] = {
def getId(e: LDAPEntry): Option[QuickSearchResultId] = {
- if (e.isA(OC_NODE)) { e(A_NODE_UUID).map(QRNodeId) }
- else if (e.isA(OC_RUDDER_NODE)) { e(A_NODE_UUID).map(QRNodeId) }
- else if (e.isA(OC_RULE)) { e(A_RULE_UUID).map(QRRuleId) }
+ if (e.isA(OC_RULE)) { e(A_RULE_UUID).map(QRRuleId) }
else if (e.isA(OC_RUDDER_NODE_GROUP)) { e(A_NODE_GROUP_UUID).map(QRGroupId) }
else if (e.isA(OC_PARAMETER)) {
e(A_PARAMETER_NAME).map(QRParameterId)
@@ -565,17 +645,7 @@ object QSLdapBackend {
if (isNodeOrNotSystem(e))
} yield {
// prefer hostname for nodes
- val defaultName = e(A_HOSTNAME).orElse(e(A_NAME)).getOrElse(id.value)
- val name = {
- if (e.isA(OC_NODE) || e.isA(OC_RUDDER_NODE)) {
- import com.normation.zio._
- getId(e)
- .flatMap(id => nodeInfos.getNodeInfo(com.normation.inventory.domain.NodeId(id.value)).runNow.map(_.hostname))
- .getOrElse(defaultName)
- } else {
- defaultName
- }
- }
+ val name = e(A_HOSTNAME).orElse(e(A_NAME)).getOrElse(id.value)
QuickSearchResult(id, name, Some(attr), desc)
}
}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchDomain.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchDomain.scala
index 2ba176bd86e..acc0c955549 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchDomain.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchDomain.scala
@@ -60,9 +60,9 @@ final case class Query(
*/
sealed trait QSBackend
object QSBackend {
- case object LdapBackend extends QSBackend
- case object DirectiveBackend extends QSBackend
-
+ final case object LdapBackend extends QSBackend
+ final case object DirectiveBackend extends QSBackend
+ final case object NodeFactBackend extends QSBackend
final val all: Set[QSBackend] = sealerate.values[QSBackend]
}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchService.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchService.scala
index 44d61ae9837..868b2c475bd 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchService.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/quicksearch/QuickSearchService.scala
@@ -43,8 +43,8 @@ import com.normation.ldap.sdk.LDAPConnectionProvider
import com.normation.ldap.sdk.RoLDAPConnection
import com.normation.rudder.domain.NodeDit
import com.normation.rudder.domain.RudderDit
+import com.normation.rudder.facts.nodes.NodeFactRepository
import com.normation.rudder.repository.RoDirectiveRepository
-import com.normation.rudder.services.nodes.NodeInfoService
import com.normation.utils.Control._
import net.liftweb.common.Box
import net.liftweb.common.EmptyBox
@@ -64,7 +64,7 @@ class FullQuickSearchService(implicit
val inventoryDit: InventoryDit,
val rudderDit: RudderDit,
val directiveRepo: RoDirectiveRepository,
- val nodeInfos: NodeInfoService
+ val nodeInfos: NodeFactRepository
) extends Loggable {
import QuickSearchService._
@@ -111,14 +111,15 @@ object QuickSearchService {
inventoryDit: InventoryDit,
nodeDit: NodeDit,
rudderDit: RudderDit,
- nodeInfos: NodeInfoService
+ nodeFactRepo: NodeFactRepository
) {
import QSBackend._
- def search(query: Query) = b match {
+ def search(query: Query): Box[Seq[QuickSearchResult]] = b match {
case LdapBackend => QSLdapBackend.search(query)
case DirectiveBackend => QSDirectiveBackend.search(query)
+ case NodeFactBackend => QSNodeFactBackend.search(query)
}
}
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/servers/PolicyServerManagementService.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/servers/PolicyServerManagementService.scala
index 78eef95f7fd..86d1670ce71 100644
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/servers/PolicyServerManagementService.scala
+++ b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/servers/PolicyServerManagementService.scala
@@ -75,6 +75,7 @@ import com.normation.rudder.domain.queries.Criterion
import com.normation.rudder.domain.queries.CriterionLine
import com.normation.rudder.domain.queries.Equals
import com.normation.rudder.domain.queries.NodeAndRootServerReturnType
+import com.normation.rudder.domain.queries.NodeCriterionMatcherString
import com.normation.rudder.domain.queries.ObjectCriterion
import com.normation.rudder.domain.queries.Query
import com.normation.rudder.domain.queries.ResultTransformation
@@ -611,9 +612,10 @@ object PolicyServerConfigurationObjects {
Criterion(
"policyServerId",
StringComparator,
+ NodeCriterionMatcherString(n => Chunk(n.rudderSettings.policyServerId.value)),
None
),
- Criterion("agentName", AgentComparator, None)
+ Criterion("agentName", AgentComparator, NodeCriterionMatcherString(n => Chunk(n.rudderAgent.agentType.id)), None)
)
)
NodeGroup(
@@ -629,13 +631,17 @@ object PolicyServerConfigurationObjects {
List(
CriterionLine(
objectType,
- Criterion("agentName", StringComparator),
+ Criterion("agentName", StringComparator, NodeCriterionMatcherString(n => Chunk(n.rudderAgent.agentType.id))),
Equals,
"cfengine"
),
CriterionLine(
objectType,
- Criterion("policyServerId", StringComparator),
+ Criterion(
+ "policyServerId",
+ StringComparator,
+ NodeCriterionMatcherString(n => Chunk(n.rudderSettings.policyServerId.value))
+ ),
Equals,
nodeId.value
)
diff --git a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/servers/ServerSummaryService.scala b/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/servers/ServerSummaryService.scala
deleted file mode 100644
index cf1c1aba248..00000000000
--- a/webapp/sources/rudder/rudder-core/src/main/scala/com/normation/rudder/services/servers/ServerSummaryService.scala
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- *************************************************************************************
- * Copyright 2011 Normation SAS
- *************************************************************************************
- *
- * This file is part of Rudder.
- *
- * Rudder is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * In accordance with the terms of section 7 (7. Additional Terms.) of
- * the GNU General Public License version 3, the copyright holders add
- * the following Additional permissions:
- * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
- * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
- * Public License version 3, when you create a Related Module, this
- * Related Module is not considered as a part of the work and may be
- * distributed under the license agreement of your choice.
- * A "Related Module" means a set of sources files including their
- * documentation that, without modification of the Source Code, enables
- * supplementary functions or services in addition to those offered by
- * the Software.
- *
- * Rudder is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Rudder. If not, see .
-
- *
- *************************************************************************************
- */
-
-package com.normation.rudder.services.servers
-
-import com.normation.box._
-import com.normation.errors.IOResult
-import com.normation.inventory.domain._
-import com.normation.inventory.ldap.core.InventoryDitService
-import com.normation.ldap.sdk._
-import com.normation.rudder.domain.servers.Srv
-import net.liftweb.common._
-import zio._
-
-trait NodeSummaryService {
-
- /**
- * Retrieve minimal information about the server
- */
- def find(status: InventoryStatus, id: NodeId*): Box[Seq[Srv]]
-}
-
-import com.normation.inventory.ldap.core._
-import com.normation.rudder.domain.RudderLDAPConstants._
-import org.joda.time.DateTime
-
-class NodeSummaryServiceImpl(
- inventoryDitService: InventoryDitService,
- inventoryMapper: InventoryMapper,
- ldap: LDAPConnectionProvider[RoLDAPConnection]
-) extends NodeSummaryService with Loggable {
-
- /**
- * build a Srv from an LDAP Entry, using a node inventory
- * for the mapping part
- */
- def makeSrv(e: LDAPEntry): IOResult[Srv] = {
-
- for {
- node <- inventoryMapper.nodeFromEntry(e)
- } yield {
- // fetch the creation datetime of the object
- val dateTime = {
- e(A_OBJECT_CREATION_DATE) match {
- case None => DateTime.now()
- case Some(date) =>
- GeneralizedTime.parse(date) match {
- case Some(value) => value.dateTime
- case None => DateTime.now()
- }
- }
- }
-
- Srv(
- id = node.main.id,
- status = node.main.status,
- hostname = node.main.hostname,
- osType = node.main.osDetails.os.kernelName,
- osName = node.main.osDetails.os.name,
- osFullName = node.main.osDetails.fullName,
- ips = node.serverIps.toList,
- creationDate = dateTime,
- isPolicyServer = e.isA(OC_POLICY_SERVER_NODE)
- )
- }
- }
-
- override def find(status: InventoryStatus, ids: NodeId*): Box[Seq[Srv]] = {
- for {
- con <- ldap
- dit = inventoryDitService.getDit(status)
- optEntries <- ZIO.foreach(ids)(id => con.get(dit.NODES.NODE.dn(id), Srv.ldapAttributes.toSeq: _*))
- srvs <- ZIO.foreach(optEntries.flatten)(e => makeSrv(e))
- } yield {
- srvs
- }
- }.toBox
-
-}
diff --git a/webapp/sources/rudder/rudder-core/src/test/resources/inventories/7.2/node2-86d9ec77-9db5-4ba3-bdca-f0baf3a5b477.ocs b/webapp/sources/rudder/rudder-core/src/test/resources/inventories/7.2/node2-86d9ec77-9db5-4ba3-bdca-f0baf3a5b477.ocs
new file mode 100644
index 00000000000..4acfdeaf688
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/resources/inventories/7.2/node2-86d9ec77-9db5-4ba3-bdca-f0baf3a5b477.ocs
@@ -0,0 +1,9332 @@
+
+
+
+
+ 2023-04-03 12:24:57
+
+
+ 12/01/2006
+ innotek GmbH
+ VirtualBox
+ Oracle Corporation
+ VirtualBox
+ 0
+ innotek GmbH
+ VirtualBox
+ 612f53b7-cdf4-f847-b938-71fe8ecc239d
+
+
+ 440FX - 82441FX PMC [Natoma]
+ Intel Corporation
+ 440FX - 82441FX PMC [Natoma]
+ 0600
+ 00:00.0
+ 1237
+ 02
+ Host bridge
+ 8086
+
+
+ 82371SB PIIX3 ISA [Natoma/Triton II]
+ Intel Corporation
+ 82371SB PIIX3 ISA [Natoma/Triton II]
+ 0601
+ 00:01.0
+ 7000
+ ISA bridge
+ 8086
+
+
+ 82371AB/EB/MB PIIX4 IDE
+ ata_piix
+ Intel Corporation
+ 82371AB/EB/MB PIIX4 IDE
+ 0101
+ 00:01.1
+ 7111
+ 01
+ IDE interface
+ 8086
+
+
+ VirtualBox Graphics Adapter
+ vboxvideo
+ InnoTek Systemberatung GmbH
+ VirtualBox Graphics Adapter
+ 0300
+ 00:02.0
+ beef
+ VGA compatible controller
+ 80ee
+
+
+ 82540EM Gigabit Ethernet Controller
+ e1000
+ Intel Corporation
+ 82540EM Gigabit Ethernet Controller
+ 0200
+ 00:03.0
+ 100e
+ 02
+ Ethernet controller
+ 8086
+
+
+ VirtualBox Guest Service
+ vboxguest
+ InnoTek Systemberatung GmbH
+ VirtualBox Guest Service
+ 0880
+ 00:04.0
+ cafe
+ System peripheral
+ 80ee
+
+
+ 82801AA AC'97 Audio Controller
+ Intel Corporation
+ 82801AA AC'97 Audio Controller
+ 0401
+ 00:05.0
+ 2415
+ 01
+ Multimedia audio controller
+ 8086
+
+
+ 82371AB/EB/MB PIIX4 ACPI
+ Intel Corporation
+ 82371AB/EB/MB PIIX4 ACPI
+ 0680
+ 00:07.0
+ 7113
+ 08
+ Bridge
+ 8086
+
+
+ 82540EM Gigabit Ethernet Controller
+ e1000
+ Intel Corporation
+ 82540EM Gigabit Ethernet Controller
+ 0200
+ 00:08.0
+ 100e
+ 02
+ Ethernet controller
+ 8086
+
+
+ 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI
+ mptspi
+ LSI Logic / Symbios Logic
+ 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI
+ 0100
+ 00:14.0
+ 0030
+ SCSI storage controller
+ 1000
+
+
+ i386
+ 1
+ 25
+ AMD
+ 68
+ AMD Ryzen 7 PRO 6850U with Radeon Graphics
+ 1
+ 1
+
+
+ ext4
+ 37926
+ d2a60571-4a8d-4496-a55f-c9303e2a11dd
+ 39630
+ /
+ /dev/sda1
+
+
+ vboxsf
+ 565605
+ 870384
+ /vagrant
+ vagrant
+
+
+ MAIL
+ /var/mail/root
+
+
+ SHLVL
+ 1
+
+
+ SUDO_COMMAND
+ /usr/bin/su
+
+
+ SUDO_UID
+ 1000
+
+
+ RUDDER_BIN
+ /usr/bin/rudder
+
+
+ _
+ /usr/bin/rudder
+
+
+ DEBIAN_FRONTEND
+ noninteractive
+
+
+ LESSCLOSE
+ /usr/bin/lesspipe %s %s
+
+
+ LS_COLORS
+ rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:
+
+
+ PWD
+ /var/rudder
+
+
+ LOGNAME
+ root
+
+
+ HOME
+ /root
+
+
+ LESSOPEN
+ | /usr/bin/lesspipe %s
+
+
+ TERM
+ xterm
+
+
+ USER
+ root
+
+
+ SHELL
+ /bin/bash
+
+
+ SUDO_USER
+ vagrant
+
+
+ BASEDIR
+ /opt/rudder/share/commands
+
+
+ SUDO_GID
+ 1000
+
+
+ PATH
+ /opt/rudder/bin:/usr/gnu/bin:/opt/rudder/bin:/usr/gnu/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin:/usr/sbin:/sbin:/usr/sbin
+
+
+ off
+
+
+ Other
+ 131071
+ Mon Apr 3 11:20
+ 10.0.2.2
+ 127.0.0.53
+ 4
+ 10.0.2.15/192.168.31.5
+ vagrant
+ 467
+ node2
+ #44-Ubuntu SMP Wed Jun 22 14:20:53 UTC 2022
+ Ubuntu 22.04 LTS
+ 5.15.0-41-generic
+ 1
+ AMD Ryzen 7 PRO 6850U with Radeon Graphics
+ vagrant
+ 612f53b7-cdf4-f847-b938-71fe8ecc239d
+ VirtualBox
+ .
+
+
+ AT Translated Set 2 keyboard
+ AT Translated Set 2 keyboard
+ sysrq
+
+
+ ImExPS/2 Generic Explorer Mouse
+ ImExPS/2 Generic Explorer Mouse
+ Pointing
+
+
+ Video Bus
+ Video Bus
+ Keyboard
+
+
+ 4
+ syslog
+ ubuntu
+ adm
+
+
+ 20
+ ubuntu
+ dialout
+
+
+ 24
+ ubuntu
+ cdrom
+
+
+ 25
+ ubuntu
+ floppy
+
+
+ 27
+ ubuntu
+ sudo
+
+
+ 29
+ ubuntu
+ audio
+
+
+ 30
+ ubuntu
+ dip
+
+
+ 44
+ ubuntu
+ video
+
+
+ 46
+ ubuntu
+ plugdev
+
+
+ 118
+ ubuntu
+ netdev
+
+
+ 119
+ ubuntu
+ lxd
+
+
+ /root
+ 0
+ root
+ root
+ /bin/bash
+
+
+ /usr/sbin
+ 1
+ daemon
+ daemon
+ /usr/sbin/nologin
+
+
+ /bin
+ 2
+ bin
+ bin
+ /usr/sbin/nologin
+
+
+ /dev
+ 3
+ sys
+ sys
+ /usr/sbin/nologin
+
+
+ /bin
+ 4
+ sync
+ sync
+ /bin/sync
+
+
+ /usr/games
+ 5
+ games
+ games
+ /usr/sbin/nologin
+
+
+ /var/cache/man
+ 6
+ man
+ man
+ /usr/sbin/nologin
+
+
+ /var/spool/lpd
+ 7
+ lp
+ lp
+ /usr/sbin/nologin
+
+
+ /var/mail
+ 8
+ mail
+ mail
+ /usr/sbin/nologin
+
+
+ /var/spool/news
+ 9
+ news
+ news
+ /usr/sbin/nologin
+
+
+ /var/spool/uucp
+ 10
+ uucp
+ uucp
+ /usr/sbin/nologin
+
+
+ /bin
+ 13
+ proxy
+ proxy
+ /usr/sbin/nologin
+
+
+ /var/www
+ 33
+ www-data
+ www-data
+ /usr/sbin/nologin
+
+
+ /var/backups
+ 34
+ backup
+ backup
+ /usr/sbin/nologin
+
+
+ /var/list
+ 38
+ list
+ Mailing List Manager
+ /usr/sbin/nologin
+
+
+ /run/ircd
+ 39
+ irc
+ ircd
+ /usr/sbin/nologin
+
+
+ /var/lib/gnats
+ 41
+ gnats
+ Gnats Bug-Reporting System (admin)
+ /usr/sbin/nologin
+
+
+ /nonexistent
+ 65534
+ nobody
+ nobody
+ /usr/sbin/nologin
+
+
+ /run/systemd
+ 100
+ systemd-network
+ systemd Network Management,,,
+ /usr/sbin/nologin
+
+
+ /run/systemd
+ 101
+ systemd-resolve
+ systemd Resolver,,,
+ /usr/sbin/nologin
+
+
+ /nonexistent
+ 102
+ messagebus
+
+ /usr/sbin/nologin
+
+
+ /run/systemd
+ 103
+ systemd-timesync
+ systemd Time Synchronization,,,
+ /usr/sbin/nologin
+
+
+ /home/syslog
+ 104
+ syslog
+
+ /usr/sbin/nologin
+
+
+ /nonexistent
+ 105
+ _apt
+
+ /usr/sbin/nologin
+
+
+ /var/lib/tpm
+ 106
+ tss
+ TPM software stack,,,
+ /bin/false
+
+
+ /run/uuidd
+ 107
+ uuidd
+
+ /usr/sbin/nologin
+
+
+ /nonexistent
+ 108
+ tcpdump
+
+ /usr/sbin/nologin
+
+
+ /run/sshd
+ 109
+ sshd
+
+ /usr/sbin/nologin
+
+
+ /var/cache/pollinate
+ 110
+ pollinate
+
+ /bin/false
+
+
+ /var/lib/landscape
+ 111
+ landscape
+
+ /usr/sbin/nologin
+
+
+ /home/vagrant
+ 1000
+ vagrant
+ ,,,
+ /bin/bash
+
+
+ /home/ubuntu
+ 1001
+ ubuntu
+ Ubuntu
+ /bin/bash
+
+
+ /var/snap/lxd/common/lxd
+ 999
+ lxd
+
+ /bin/false
+
+
+ AP///////wBYWAAAAAQAAwEAAQOAAAB47u6Ro1RMmSYPUFQAAAABAQEBAQEBAQEBAQEBAQEBrxIABkAABjACAgICAAAAAAAAAAAA/QAAyADIZAAKICAgICAgAAAA/ABWQk9YIG1vbml0b3IKAAAAEAAKICAgICAgICAgICAgABk=
+ VBOX monitor
+ 1/1990
+ VBX
+ 03000400
+
+
+ lo
+ 127.0.0.1
+ 255.0.0.0
+ 127.0.0.0
+ 00:00:00:00:00:00
+ Up
+ loopback
+ 1
+
+
+ lo
+ ::1
+ fff0::
+ ::
+ 00:00:00:00:00:00
+ Up
+ loopback
+ 1
+
+
+ enp0s3
+ e1000
+ 10.0.2.15
+ 10.0.2.2
+ 255.255.255.0
+ 10.0.2.0
+ 02:e5:1b:70:f4:63
+ 8086:100E:8086:001E
+ 0000:00:03.0
+ 1000
+ Up
+ ethernet
+ 0
+
+
+ enp0s3
+ e1000
+ fe80::e5:1bff:fe70:f463
+ ffff:ffff:ffff:ffff::
+ fe80::
+ 02:e5:1b:70:f4:63
+ 8086:100E:8086:001E
+ 0000:00:03.0
+ 1000
+ Up
+ ethernet
+ 0
+
+
+ enp0s8
+ e1000
+ 192.168.31.5
+ 255.255.255.0
+ 192.168.31.0
+ 08:00:27:df:72:2b
+ 8086:100E:8086:001E
+ 0000:00:08.0
+ 1000
+ Up
+ ethernet
+ 0
+
+
+ enp0s8
+ e1000
+ fe80::a00:27ff:fedf:722b
+ ffff:ffff:ffff:ffff::
+ fe80::
+ 08:00:27:df:72:2b
+ 8086:100E:8086:001E
+ 0000:00:08.0
+ 1000
+ Up
+ ethernet
+ 0
+
+
+ x86_64
+ 2023-04-03 09:23:27
+ .
+ node2..
+ Ubuntu 22.04 LTS
+ a8c0051f
+ linux
+ 5.15.0-41-generic
+ Ubuntu
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDGmRRdJl/vGT1+/kVhAvjPLFMiLxs7Lu7qkhFaqfAABrdXw0Q8NgyKGuht3isb0zJzMWPd1Sa63IKPZqtTcU/0mduGlc44mx0KcUPZZwRAUHh+/MQkz7jJjOWF/Ckb4CLXruBcA2fU5VvdkpUBvZt4FgYBWG0GuQPrxiI2J+zDHi7F6QpFMWewYcgwaXHM0g2NRDF3cjCl/UHajue+E7GZgklsRDb+Bg3jLPBzwhVR/6m2Xf2JBph/5ghm2ZC6W1RBnBSeSboZIfj4iONzywvR78m+DR6IykSIkjFRPdoXhmA64RzSB2qLyz6w++AssXRZgJaRD7BTOSLz9sLB2y7IVmRVhtMEvmNz5gwug2kE92IPymcT+pduSYtEBUNRrQhuDuPrN49xdSg2nVwmVfZymAiwX4l87mt0zQ7hywuAtpbJLvM2d2/Y9WUg2cUP8GGcNJyaYhO3NRGyKT1GCzkq5yyRxBvW5qCYv2WtstKFKOyxJ0akaj3Ft69te3UBzBk=
+
+ UTC
+ +0000
+
+ 22.04
+
+
+ /sbin/init
+ 0.0
+ 1.9
+ 1
+ 2023-04-03 09:23
+ ?
+ root
+ 167496
+
+
+ [kthreadd]
+ 0.0
+ 0.0
+ 2
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [rcu_gp]
+ 0.0
+ 0.0
+ 3
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [rcu_par_gp]
+ 0.0
+ 0.0
+ 4
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [netns]
+ 0.0
+ 0.0
+ 5
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kworker/0:0-events]
+ 0.0
+ 0.0
+ 6
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kworker/0:0H-events_highpri]
+ 0.0
+ 0.0
+ 7
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [mm_percpu_wq]
+ 0.0
+ 0.0
+ 10
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [rcu_tasks_rude_]
+ 0.0
+ 0.0
+ 11
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [rcu_tasks_trace]
+ 0.0
+ 0.0
+ 12
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [ksoftirqd/0]
+ 0.0
+ 0.0
+ 13
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [rcu_sched]
+ 0.0
+ 0.0
+ 14
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [migration/0]
+ 0.0
+ 0.0
+ 15
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [idle_inject/0]
+ 0.0
+ 0.0
+ 16
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [cpuhp/0]
+ 0.0
+ 0.0
+ 17
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kdevtmpfs]
+ 0.0
+ 0.0
+ 18
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [inet_frag_wq]
+ 0.0
+ 0.0
+ 19
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kauditd]
+ 0.0
+ 0.0
+ 20
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [khungtaskd]
+ 0.0
+ 0.0
+ 21
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [oom_reaper]
+ 0.0
+ 0.0
+ 22
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [writeback]
+ 0.0
+ 0.0
+ 23
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kcompactd0]
+ 0.0
+ 0.0
+ 24
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [ksmd]
+ 0.0
+ 0.0
+ 25
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kintegrityd]
+ 0.0
+ 0.0
+ 71
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kblockd]
+ 0.0
+ 0.0
+ 72
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [blkcg_punt_bio]
+ 0.0
+ 0.0
+ 73
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [tpm_dev_wq]
+ 0.0
+ 0.0
+ 74
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [ata_sff]
+ 0.0
+ 0.0
+ 75
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [md]
+ 0.0
+ 0.0
+ 76
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [edac-poller]
+ 0.0
+ 0.0
+ 77
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [devfreq_wq]
+ 0.0
+ 0.0
+ 78
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [watchdogd]
+ 0.0
+ 0.0
+ 79
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kworker/0:1H-kblockd]
+ 0.0
+ 0.0
+ 81
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kswapd0]
+ 0.0
+ 0.0
+ 83
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [ecryptfs-kthrea]
+ 0.0
+ 0.0
+ 84
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kthrotld]
+ 0.0
+ 0.0
+ 86
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [acpi_thermal_pm]
+ 0.0
+ 0.0
+ 87
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [scsi_eh_0]
+ 0.0
+ 0.0
+ 89
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [scsi_tmf_0]
+ 0.0
+ 0.0
+ 90
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [scsi_eh_1]
+ 0.0
+ 0.0
+ 91
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [scsi_tmf_1]
+ 0.0
+ 0.0
+ 92
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [vfio-irqfd-clea]
+ 0.0
+ 0.0
+ 94
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [mld]
+ 0.0
+ 0.0
+ 95
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [ipv6_addrconf]
+ 0.0
+ 0.0
+ 96
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kstrp]
+ 0.0
+ 0.0
+ 106
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [zswap-shrink]
+ 0.0
+ 0.0
+ 109
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kworker/u3:0]
+ 0.0
+ 0.0
+ 110
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [charger_manager]
+ 0.0
+ 0.0
+ 115
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [cryptd]
+ 0.0
+ 0.0
+ 154
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [mpt_poll_0]
+ 0.0
+ 0.0
+ 159
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [mpt/0]
+ 0.0
+ 0.0
+ 169
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [scsi_eh_2]
+ 0.0
+ 0.0
+ 188
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [scsi_tmf_2]
+ 0.0
+ 0.0
+ 189
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [raid5wq]
+ 0.0
+ 0.0
+ 221
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [jbd2/sda1-8]
+ 0.0
+ 0.0
+ 265
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [ext4-rsv-conver]
+ 0.0
+ 0.0
+ 266
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ /lib/systemd/systemd-journald
+ 0.0
+ 1.7
+ 338
+ 2023-04-03 09:23
+ ?
+ root
+ 48392
+
+
+ [ipmi-msghandler]
+ 0.0
+ 0.0
+ 361
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kaluad]
+ 0.0
+ 0.0
+ 372
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kmpath_rdacd]
+ 0.0
+ 0.0
+ 375
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kmpathd]
+ 0.0
+ 0.0
+ 377
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ [kmpath_handlerd]
+ 0.0
+ 0.0
+ 378
+ 2023-04-03 09:23
+ ?
+ root
+ 0
+
+
+ /sbin/multipathd -d -s
+ 0.0
+ 5.6
+ 380
+ 2023-04-03 09:23
+ ?
+ root
+ 354888
+
+
+ /lib/systemd/systemd-udevd
+ 0.0
+ 1.0
+ 383
+ 2023-04-03 09:23
+ ?
+ root
+ 23172
+
+
+ /lib/systemd/systemd-timesyncd
+ 0.0
+ 0.7
+ 418
+ 2023-04-03 09:23
+ ?
+ systemd+
+ 89352
+
+
+ /lib/systemd/systemd-resolved
+ 0.0
+ 1.7
+ 557
+ 2023-04-03 09:23
+ ?
+ systemd+
+ 25392
+
+
+ /usr/sbin/cron -f -P
+ 0.0
+ 0.5
+ 666
+ 2023-04-03 09:23
+ ?
+ root
+ 7284
+
+
+ @dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only
+ 0.0
+ 0.8
+ 668
+ 2023-04-03 09:23
+ ?
+ message+
+ 8884
+
+
+ /usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers
+ 0.0
+ 2.3
+ 674
+ 2023-04-03 09:23
+ ?
+ root
+ 32992
+
+
+ /usr/libexec/polkitd --no-debug
+ 0.0
+ 0.7
+ 675
+ 2023-04-03 09:23
+ ?
+ root
+ 234484
+
+
+ /usr/sbin/rsyslogd -n -iNONE
+ 0.0
+ 1.0
+ 680
+ 2023-04-03 09:23
+ ?
+ syslog
+ 222400
+
+
+ /usr/lib/snapd/snapd
+ 0.0
+ 3.5
+ 682
+ 2023-04-03 09:23
+ ?
+ root
+ 734956
+
+
+ /lib/systemd/systemd-logind
+ 0.0
+ 0.9
+ 683
+ 2023-04-03 09:23
+ ?
+ root
+ 23716
+
+
+ /usr/libexec/udisks2/udisksd
+ 0.0
+ 1.5
+ 689
+ 2023-04-03 09:23
+ ?
+ root
+ 392696
+
+
+ /usr/sbin/ModemManager
+ 0.0
+ 1.0
+ 732
+ 2023-04-03 09:23
+ ?
+ root
+ 316936
+
+
+ /sbin/agetty -o -p -- \u --keep-baud 115200,57600,38400,9600 ttyS0 vt220
+ 0.0
+ 0.1
+ 744
+ 2023-04-03 09:23
+ ttyS0
+ root
+ 6216
+
+
+ /sbin/agetty -o -p -- \u --noclear tty1 linux
+ 0.0
+ 0.1
+ 772
+ 2023-04-03 09:23
+ tty1
+ root
+ 6172
+
+
+ /usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal
+ 0.0
+ 2.7
+ 775
+ 2023-04-03 09:23
+ ?
+ root
+ 110088
+
+
+ sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
+ 0.0
+ 1.2
+ 799
+ 2023-04-03 09:23
+ ?
+ root
+ 15416
+
+
+ /lib/systemd/systemd-networkd
+ 0.0
+ 0.7
+ 1710
+ 2023-04-03 09:23
+ ?
+ systemd+
+ 16248
+
+
+ /usr/libexec/packagekitd
+ 0.0
+ 1.9
+ 2323
+ 2023-04-03 09:24
+ ?
+ root
+ 295940
+
+
+ /opt/rudder/bin/cf-execd --no-fork
+ 0.0
+ 2.7
+ 5025
+ 2023-04-03 09:24
+ ?
+ root
+ 31884
+
+
+ /opt/rudder/bin/cf-serverd --graceful-detach=600 --no-fork --inform
+ 0.0
+ 2.2
+ 5026
+ 2023-04-03 09:24
+ ?
+ root
+ 31900
+
+
+ sshd: vagrant [priv]
+ 0.0
+ 1.2
+ 11292
+ 2023-04-03 11:19
+ ?
+ root
+ 16908
+
+
+ /lib/systemd/systemd --user
+ 0.0
+ 1.0
+ 11295
+ 2023-04-03 11:19
+ ?
+ vagrant
+ 17036
+
+
+ (sd-pam)
+ 0.0
+ 1.0
+ 11296
+ 2023-04-03 11:19
+ ?
+ vagrant
+ 170404
+
+
+ sshd: vagrant@pts/0
+ 0.0
+ 1.0
+ 11378
+ 2023-04-03 11:20
+ ?
+ vagrant
+ 17420
+
+
+ -bash
+ 0.0
+ 0.8
+ 11379
+ 2023-04-03 11:20
+ pts/0
+ vagrant
+ 9148
+
+
+ sudo su
+ 0.0
+ 0.7
+ 11440
+ 2023-04-03 11:20
+ pts/0
+ root
+ 11888
+
+
+ sudo su
+ 0.0
+ 0.1
+ 11441
+ 2023-04-03 11:20
+ pts/1
+ root
+ 11888
+
+
+ su
+ 0.0
+ 0.5
+ 11442
+ 2023-04-03 11:20
+ pts/1
+ root
+ 10592
+
+
+ bash
+ 0.0
+ 0.7
+ 11443
+ 2023-04-03 11:20
+ pts/1
+ root
+ 8024
+
+
+ /bin/sh /snap/lxd/22923/commands/daemon.start
+ 0.0
+ 0.0
+ 13629
+ 2023-04-03 11:35
+ ?
+ root
+ 2624
+
+
+ lxcfs /var/snap/lxd/common/var/lib/lxcfs -p /var/snap/lxd/common/lxcfs.pid
+ 0.0
+ 0.0
+ 13781
+ 2023-04-03 11:35
+ ?
+ root
+ 85652
+
+
+ lxd --logfile /var/snap/lxd/common/lxd/logs/lxd.log --group lxd
+ 0.1
+ 5.7
+ 13792
+ 2023-04-03 11:35
+ ?
+ root
+ 1392196
+
+
+ [kworker/0:1]
+ 0.0
+ 0.0
+ 13801
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [dio/sda1]
+ 0.0
+ 0.0
+ 13897
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ bpfilter_umh
+ 0.0
+ 0.1
+ 13913
+ 2023-04-03 11:35
+ ?
+ root
+ 2772
+
+
+ [spl_system_task]
+ 0.0
+ 0.0
+ 13942
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [spl_delay_taskq]
+ 0.0
+ 0.0
+ 13943
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [spl_dynamic_tas]
+ 0.0
+ 0.0
+ 13944
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [spl_kmem_cache]
+ 0.0
+ 0.0
+ 13945
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [zvol]
+ 0.0
+ 0.0
+ 13946
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [arc_prune]
+ 0.0
+ 0.0
+ 13947
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [arc_evict]
+ 0.0
+ 0.0
+ 13948
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [arc_reap]
+ 0.0
+ 0.0
+ 13949
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [dbu_evict]
+ 0.0
+ 0.0
+ 13950
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [dbuf_evict]
+ 0.0
+ 0.0
+ 13951
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [z_vdev_file]
+ 0.0
+ 0.0
+ 13952
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [l2arc_feed]
+ 0.0
+ 0.0
+ 13953
+ 2023-04-03 11:35
+ ?
+ root
+ 0
+
+
+ [kworker/u2:1-events_unbound]
+ 0.0
+ 0.0
+ 15918
+ 2023-04-03 11:56
+ ?
+ root
+ 0
+
+
+ [kworker/u2:0-events_unbound]
+ 0.0
+ 0.0
+ 17297
+ 2023-04-03 12:15
+ ?
+ root
+ 0
+
+
+ /bin/sh /opt/rudder/share/commands/agent-inventory
+ 0.0
+ 0.3
+ 18000
+ 2023-04-03 12:24
+ pts/1
+ root
+ 2888
+
+
+ /bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory
+ 0.0
+ 0.3
+ 18040
+ 2023-04-03 12:24
+ pts/1
+ root
+ 2888
+
+
+ /opt/rudder/bin/cf-agent -I -D info -Cnever -K -b doInventory -D force_inventory
+ 38.0
+ 5.5
+ 18133
+ 2023-04-03 12:24
+ ?
+ root
+ 74644
+
+
+ cat
+ 0.0
+ 0.2
+ 18134
+ 2023-04-03 12:24
+ pts/1
+ root
+ 6328
+
+
+ cat
+ 0.0
+ 0.2
+ 18135
+ 2023-04-03 12:24
+ pts/1
+ root
+ 6328
+
+
+ cat
+ 0.0
+ 0.2
+ 18136
+ 2023-04-03 12:24
+ pts/1
+ root
+ 6328
+
+
+ /bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory
+ 0.0
+ 0.0
+ 18137
+ 2023-04-03 12:24
+ pts/1
+ root
+ 2888
+
+
+ awk -v info=0 -v full_strings=0 -v summary_only=0 -v quiet=0 -v multihost=0 -v green=\033[1;32m -v darkgreen=\033[0;32m -v red=\033[1;31m -v yellow=\033[1;33m -v magenta=\033[1;35m -v normal=\033[0;39m\033[0;49m -v white=\033[0;02m -v cyan=\033[1;36m -v dblue=\033[0;34m -v dgreen=\033[0;32m -v timing=0 -v has_fflush=OK -v full_compliance=1 -v partial_run=1 -v error_fail=0 -v noncompliant_fail=0 -f /opt/rudder/share/commands/../lib/reports.awk
+ 0.0
+ 0.8
+ 18138
+ 2023-04-03 12:24
+ pts/1
+ root
+ 11880
+
+
+ /bin/sh /opt/rudder/bin/run-inventory --local=/var/rudder/tmp/inventory/node2-86d9ec77-9db5-4ba3-bdca-f0baf3a5b477.ocs
+ 0.0
+ 0.1
+ 18228
+ 2023-04-03 12:24
+ ?
+ root
+ 2888
+
+
+ /bin/sh /opt/rudder/bin/rudder-perl -I /opt/rudder/lib/perl5 /opt/rudder/bin/fusioninventory-agent --config=none --no-task=Deploy --local=/var/rudder/tmp/inventory/node2-86d9ec77-9db5-4ba3-bdca-f0baf3a5b477.ocs
+ 0.0
+ 0.2
+ 18232
+ 2023-04-03 12:24
+ ?
+ root
+ 2888
+
+
+ fusioninventory-agent: running task Inventory
+ 20.0
+ 10.0
+ 18233
+ 2023-04-03 12:24
+ ?
+ root
+ 50448
+
+
+ ps -A -o user,pid,pcpu,pmem,vsz,tty,etime,command
+ 0.0
+ 0.3
+ 18246
+ 2023-04-03 12:24
+ ?
+ root
+ 7060
+
+
+
+ -----BEGIN CERTIFICATE-----
+MIIFqzCCA5OgAwIBAgIUBy0WHdwXwVZr1241fqZ3dHTT00kwDQYJKoZIhvcNAQEL
+BQAwNjE0MDIGCgmSJomT8ixkAQEMJDg2ZDllYzc3LTlkYjUtNGJhMy1iZGNhLWYw
+YmFmM2E1YjQ3NzAeFw0yMzA0MDMwOTI0NTdaFw0zMzAzMzEwOTI0NTdaMDYxNDAy
+BgoJkiaJk/IsZAEBDCQ4NmQ5ZWM3Ny05ZGI1LTRiYTMtYmRjYS1mMGJhZjNhNWI0
+NzcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDilIBlWT+TIz13776N
+QJSnKjnHyADqjXK8WVidt4xvYoP+Dcp5bAA3qrBdLxopIT/x2NBqXhaQoBeWTpdJ
+tiBvCCjTZTBB85XkyPalNQ4DOyQwdO/mTdth1GXxyAScTyb5LmPzGt5HWUyubHpS
+vPvHt5YzeXaP7Jrq5CMv9nKp4WTWHhGGRMwpw0JM4o5veEVUjUjNWuo4McW06Ntt
+Ze6nHW2L42i9uCOmd1w66mP+FXVYRLQSW3VWel/gjnsJqFavzk1PUqIBZWyPpsEK
+mS5ptzF2Pwnez1rYGBtdJdzwdF6FmbErbEsqAS+EZYCCaW6ilqw7M/Cpg6S9236i
+iCSg0Z4MCceiSd/aFlPVBgCpM85j+IvrgIGvoe4VPF7HzEpK5cPiSo8v53k0h4FL
+dDO5fWUCXNFxWekcbJrh7Xw36st328w4xLlVqvSWY4q2pNPDsq1y9G92TNaupyRM
+Hb9jgW5DoXF2S53gQ2LLyRX8egHHHHxrTLbU8V9yrQadkn4qkubguncGobWiOGbX
+xK8Oo9QccGIVzSZ8TztkfzRtYNAGkDQ+jY0GcjDQCrri8V3B867vDB0IdGc+k+rn
+BNuJCQNGs5F4Fyi2X48tNrR3nMy8VYzXaboMVHmG/QbZR4iHowGUJ8DW52qHRamV
+KrQlOCYfhHrs/VuXSoQXsY9ozQIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYD
+VR0OBBYEFExcqlVR4NA/Pkv4LVs62vM+QvdTMHEGA1UdIwRqMGiAFExcqlVR4NA/
+Pkv4LVs62vM+QvdToTqkODA2MTQwMgYKCZImiZPyLGQBAQwkODZkOWVjNzctOWRi
+NS00YmEzLWJkY2EtZjBiYWYzYTViNDc3ghQHLRYd3BfBVmvXbjV+pnd0dNPTSTAL
+BgNVHQ8EBAMCArwwDQYJKoZIhvcNAQELBQADggIBABHGoFglYKYCoXUtJEWiDD5E
+HsYn0xGGoXXLOi82vRY9hyXLu1GqVHuIgP9RqvU3TUr/bFVYa6GDIUp890C1nH6o
+k+SfCp7TW3OqvxEYmXTgLcXOveP2wVfd1YwVwPbsdU69MaMw1AxNOZX1e1Feb1SF
+IkSO7Dmvv0I2pR7X4Ur8AHuTLfxKg4CfHED/51vJ0bXuA5IU+zaByioN3s1/Mf6J
+37fOVXFuwP6Y0gbQdN2Qze4OM69m/GbIdYGcncbIAE2pCH4M3QOevyNAEyo87WYC
+AKlU/SPswyUH2qtvzR5uzrskBvYOT5PJkD7AwHMcKbKL9ifwYDYproScynKoWb/T
+9x5dBr1z+zzmYFvs+UMTLwD/jckztIs765gW/NQzCGvOQRv0TvWa4ezdxRrbMVgD
+ENEQTED82JwDVHU8hNhuK2KKPdtCZ7ulRSGtCEFta++nvuKUkXMopZ/huS0j9/M7
+tDvSMtkQOXih3v5EO2WrG+zhueYA1Rw+TyZHI03PCy7oIaKvLqre2su/1LQVil2V
+NKRq4Fibk09OuvNMEozyNzLecKd3XpLF8TCrBkW86ubdceHft7PWuJpOHTW60U6L
+kwkQJKKFHXqX4AiBiqRJLbFX0od6iSaolcfBgOQXvLsNM+zz2qlOFafj7Z0eqN9Y
+WIyBsNQ1oYnd5elHILQ2
+-----END CERTIFICATE-----
+
+ cfengine-community
+ -----BEGIN RSA PUBLIC KEY-----
+MIICCgKCAgEA4pSAZVk/kyM9d+++jUCUpyo5x8gA6o1yvFlYnbeMb2KD/g3KeWwA
+N6qwXS8aKSE/8djQal4WkKAXlk6XSbYgbwgo02UwQfOV5Mj2pTUOAzskMHTv5k3b
+YdRl8cgEnE8m+S5j8xreR1lMrmx6Urz7x7eWM3l2j+ya6uQjL/ZyqeFk1h4RhkTM
+KcNCTOKOb3hFVI1IzVrqODHFtOjbbWXupx1ti+NovbgjpndcOupj/hV1WES0Elt1
+Vnpf4I57CahWr85NT1KiAWVsj6bBCpkuabcxdj8J3s9a2BgbXSXc8HRehZmxK2xL
+KgEvhGWAgmluopasOzPwqYOkvdt+oogkoNGeDAnHoknf2hZT1QYAqTPOY/iL64CB
+r6HuFTxex8xKSuXD4kqPL+d5NIeBS3QzuX1lAlzRcVnpHGya4e18N+rLd9vMOMS5
+Var0lmOKtqTTw7KtcvRvdkzWrqckTB2/Y4FuQ6Fxdkud4ENiy8kV/HoBxxx8a0y2
+1PFfcq0GnZJ+KpLm4Lp3BqG1ojhm18SvDqPUHHBiFc0mfE87ZH80bWDQBpA0Po2N
+BnIw0Aq64vFdwfOu7wwdCHRnPpPq5wTbiQkDRrOReBcotl+PLTa0d5zMvFWM12m6
+DFR5hv0G2UeIh6MBlCfA1udqh0WplSq0JTgmH4R67P1bl0qEF7GPaM0CAwEAAQ==
+-----END RSA PUBLIC KEY-----
+
+ root
+ relay
+ 3f74e095-f2b9-4357-bdc2-d6abdf4366a0
+
+
+ cfengine
+ jq
+ yaml
+ xml
+ curl
+ http_reporting
+ acl
+
+ node2.rudder.local
+
+ 86d9ec77-9db5-4ba3-bdca-f0baf3a5b477
+
+
+ all
+ 608
+ deb
+ adduser
+ Ubuntu
+
+ 3.118ubuntu5
+ admin
+ 3.118ubuntu5
+
+
+ amd64
+ 2664
+ deb
+ apparmor
+ Ubuntu
+
+ 3.0.4-2ubuntu2.1
+ admin
+ 3.0.4-2ubuntu2.1
+
+
+ all
+ 812
+ deb
+ apport
+ Ubuntu
+
+ 2.20.11-0ubuntu82.1
+ utils
+ 2.20.11-0ubuntu82.1
+
+
+ all
+ 61
+ deb
+ apport-symptoms
+ Ubuntu
+
+ 0.24
+ utils
+ 0.24
+
+
+ amd64
+ 4156
+ deb
+ apt
+ Ubuntu
+
+ 2.4.5
+ admin
+ 2.4.5
+
+
+ all
+ 165
+ deb
+ apt-transport-https
+ Ubuntu
+ apt
+ 2.4.8
+ oldlibs
+ 2.4.8
+
+
+ amd64
+ 788
+ deb
+ apt-utils
+ Ubuntu
+ apt
+ 2.4.5
+ admin
+ 2.4.5
+
+
+ all
+ 1861
+ deb
+ augeas-lenses
+ Ubuntu
+ augeas
+ 1.13.0-1
+ misc
+ 1.13.0-1
+
+
+ amd64
+ 105
+ deb
+ augeas-tools
+ Ubuntu
+ augeas
+ 1.13.0-1
+ admin
+ 1.13.0-1
+
+
+ amd64
+ 394
+ deb
+ base-files
+ Ubuntu
+
+ 12ubuntu4.1
+ admin
+ 12ubuntu4.1
+
+
+ amd64
+ 243
+ deb
+ base-passwd
+ Ubuntu
+
+ 3.5.52build1
+ admin
+ 3.5.52build1
+
+
+ amd64
+ 1864
+ deb
+ bash
+ Ubuntu
+
+ 5.1-6ubuntu1
+ shells
+ 5.1-6ubuntu1
+
+
+ all
+ 1464
+ deb
+ bash-completion
+ Ubuntu
+
+ 1:2.11-5ubuntu1
+ shells
+ 1:2.11-5ubuntu1
+
+
+ amd64
+ 215
+ deb
+ bc
+ Ubuntu
+
+ 1.07.1-3build1
+ math
+ 1.07.1-3build1
+
+
+ amd64
+ 107
+ deb
+ bcache-tools
+ Ubuntu
+
+ 1.0.8-4ubuntu3
+ utils
+ 1.0.8-4ubuntu3
+
+
+ amd64
+ 484
+ deb
+ bind9-dnsutils
+ Ubuntu
+ bind9
+ 1:9.18.1-1ubuntu1.1
+ net
+ 1:9.18.1-1ubuntu1.1
+
+
+ amd64
+ 156
+ deb
+ bind9-host
+ Ubuntu
+ bind9
+ 1:9.18.1-1ubuntu1.1
+ net
+ 1:9.18.1-1ubuntu1.1
+
+
+ amd64
+ 8492
+ deb
+ bind9-libs
+ Ubuntu
+ bind9
+ 1:9.18.1-1ubuntu1.1
+ libs
+ 1:9.18.1-1ubuntu1.1
+
+
+ amd64
+ 112
+ deb
+ binutils
+ Ubuntu
+
+ 2.38-4ubuntu2.1
+ devel
+ 2.38-4ubuntu2.1
+
+
+ amd64
+ 504
+ deb
+ binutils-common
+ Ubuntu
+ binutils
+ 2.38-4ubuntu2.1
+ devel
+ 2.38-4ubuntu2.1
+
+
+ amd64
+ 10439
+ deb
+ binutils-x86-64-linux-gnu
+ Ubuntu
+ binutils
+ 2.38-4ubuntu2.1
+ devel
+ 2.38-4ubuntu2.1
+
+
+ amd64
+ 469
+ deb
+ bolt
+ Ubuntu
+
+ 0.9.2-1
+ admin
+ 0.9.2-1
+
+
+ amd64
+ 337
+ deb
+ bsdextrautils
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ utils
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 334
+ deb
+ bsdutils
+ Ubuntu
+ util-linux (2.37.2-4ubuntu3)
+ 2.37.2-4ubuntu3
+ utils
+ 1:2.37.2-4ubuntu3
+
+
+ amd64
+ 4190
+ deb
+ btrfs-progs
+ Ubuntu
+
+ 5.16.2-1
+ admin
+ 5.16.2-1
+
+
+ amd64
+ 361
+ deb
+ busybox-initramfs
+ Ubuntu
+ busybox
+ 1:1.30.1-7ubuntu3
+ shells
+ 1:1.30.1-7ubuntu3
+
+
+ amd64
+ 2245
+ deb
+ busybox-static
+ Ubuntu
+ busybox
+ 1:1.30.1-7ubuntu3
+ shells
+ 1:1.30.1-7ubuntu3
+
+
+ all
+ 624
+ deb
+ byobu
+ Ubuntu
+
+ 5.133-1
+ misc
+ 5.133-1
+
+
+ all
+ 375
+ deb
+ ca-certificates
+ Ubuntu
+
+ 20211016
+ misc
+ 20211016
+
+
+ all
+ 65
+ deb
+ cloud-guest-utils
+ Ubuntu
+ cloud-utils
+ 0.32-22-g45fe84a5-0ubuntu1
+ admin
+ 0.32-22-g45fe84a5-0ubuntu1
+
+
+ all
+ 2563
+ deb
+ cloud-init
+ Ubuntu
+
+ 22.2-0ubuntu1~22.04.3
+ admin
+ 22.2-0ubuntu1~22.04.3
+
+
+ all
+ 25
+ deb
+ cloud-initramfs-copymods
+ Ubuntu
+ cloud-initramfs-tools
+ 0.47ubuntu1
+ admin
+ 0.47ubuntu1
+
+
+ all
+ 31
+ deb
+ cloud-initramfs-dyn-netconf
+ Ubuntu
+ cloud-initramfs-tools
+ 0.47ubuntu1
+ admin
+ 0.47ubuntu1
+
+
+ all
+ 37
+ deb
+ command-not-found
+ Ubuntu
+
+ 22.04.0
+ admin
+ 22.04.0
+
+
+ all
+ 426
+ deb
+ console-setup
+ Ubuntu
+
+ 1.205ubuntu3
+ utils
+ 1.205ubuntu3
+
+
+ all
+ 2171
+ deb
+ console-setup-linux
+ Ubuntu
+ console-setup
+ 1.205ubuntu3
+ utils
+ 1.205ubuntu3
+
+
+ amd64
+ 7112
+ deb
+ coreutils
+ Ubuntu
+
+ 8.32-4.1ubuntu1
+ utils
+ 8.32-4.1ubuntu1
+
+
+ amd64
+ 324
+ deb
+ cpio
+ Ubuntu
+
+ 2.13+dfsg-7
+ utils
+ 2.13+dfsg-7
+
+
+ amd64
+ 255
+ deb
+ cron
+ Ubuntu
+
+ 3.0pl1-137ubuntu3
+ admin
+ 3.0pl1-137ubuntu3
+
+
+ amd64
+ 487
+ deb
+ cryptsetup
+ Ubuntu
+
+ 2:2.4.3-1ubuntu1
+ admin
+ 2:2.4.3-1ubuntu1
+
+
+ amd64
+ 596
+ deb
+ cryptsetup-bin
+ Ubuntu
+ cryptsetup
+ 2:2.4.3-1ubuntu1
+ admin
+ 2:2.4.3-1ubuntu1
+
+
+ all
+ 155
+ deb
+ cryptsetup-initramfs
+ Ubuntu
+ cryptsetup
+ 2:2.4.3-1ubuntu1
+ admin
+ 2:2.4.3-1ubuntu1
+
+
+ amd64
+ 443
+ deb
+ curl
+ Ubuntu
+
+ 7.81.0-1ubuntu1.10
+ web
+ 7.81.0-1ubuntu1.10
+
+
+ amd64
+ 214
+ deb
+ dash
+ Ubuntu
+
+ 0.5.11+git20210903+057cd650a4ed-3build1
+ shells
+ 0.5.11+git20210903+057cd650a4ed-3build1
+
+
+ amd64
+ 582
+ deb
+ dbus
+ Ubuntu
+
+ 1.12.20-2ubuntu4
+ admin
+ 1.12.20-2ubuntu4
+
+
+ amd64
+ 130
+ deb
+ dbus-user-session
+ Ubuntu
+ dbus
+ 1.12.20-2ubuntu4
+ admin
+ 1.12.20-2ubuntu4
+
+
+ all
+ 512
+ deb
+ debconf
+ Ubuntu
+
+ 1.5.79ubuntu1
+ admin
+ 1.5.79ubuntu1
+
+
+ all
+ 787
+ deb
+ debconf-i18n
+ Ubuntu
+ debconf
+ 1.5.79ubuntu1
+ localization
+ 1.5.79ubuntu1
+
+
+ amd64
+ 243
+ deb
+ debianutils
+ Ubuntu
+
+ 5.5-1ubuntu2
+ utils
+ 5.5-1ubuntu2
+
+
+ amd64
+ 424
+ deb
+ diffutils
+ Ubuntu
+
+ 1:3.8-0ubuntu2
+ utils
+ 1:3.8-0ubuntu2
+
+
+ amd64
+ 676
+ deb
+ dirmngr
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 69
+ deb
+ distro-info
+ Ubuntu
+
+ 1.1build1
+ devel
+ 1.1build1
+
+
+ all
+ 19
+ deb
+ distro-info-data
+ Ubuntu
+
+ 0.52ubuntu0.1
+ devel
+ 0.52ubuntu0.1
+
+
+ amd64
+ 245
+ deb
+ dmeventd
+ Ubuntu
+ lvm2 (2.03.11-2.1ubuntu4)
+ 2.03.11-2.1ubuntu4
+ admin
+ 2:1.02.175-2.1ubuntu4
+
+
+ amd64
+ 199
+ deb
+ dmidecode
+ Ubuntu
+
+ 3.3-3
+ utils
+ 3.3-3
+
+
+ amd64
+ 273
+ deb
+ dmsetup
+ Ubuntu
+ lvm2 (2.03.11-2.1ubuntu4)
+ 2.03.11-2.1ubuntu4
+ admin
+ 2:1.02.175-2.1ubuntu4
+
+
+ amd64
+ 245
+ deb
+ dosfstools
+ Ubuntu
+
+ 4.2-1build3
+ otherosfs
+ 4.2-1build3
+
+
+ amd64
+ 6733
+ deb
+ dpkg
+ Ubuntu
+
+ 1.21.1ubuntu2.1
+ admin
+ 1.21.1ubuntu2.1
+
+
+ amd64
+ 1516
+ deb
+ e2fsprogs
+ Ubuntu
+
+ 1.46.5-2ubuntu1.1
+ admin
+ 1.46.5-2ubuntu1.1
+
+
+ all
+ 25
+ deb
+ eatmydata
+ Ubuntu
+ libeatmydata
+ 130-2build1
+ utils
+ 130-2build1
+
+
+ amd64
+ 108
+ deb
+ ed
+ Ubuntu
+
+ 1.18-1
+ editors
+ 1.18-1
+
+
+ amd64
+ 152
+ deb
+ eject
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ utils
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 630
+ deb
+ ethtool
+ Ubuntu
+
+ 1:5.16-1
+ net
+ 1:5.16-1
+
+
+ amd64
+ 437
+ deb
+ fdisk
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ utils
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 83
+ deb
+ file
+ Ubuntu
+
+ 1:5.41-3
+ utils
+ 1:5.41-3
+
+
+ all
+ 30
+ deb
+ finalrd
+ Ubuntu
+
+ 9build1
+ utils
+ 9build1
+
+
+ amd64
+ 620
+ deb
+ findutils
+ Ubuntu
+
+ 4.8.0-1ubuntu3
+ utils
+ 4.8.0-1ubuntu3
+
+
+ all
+ 63
+ deb
+ fonts-ubuntu-console
+ Ubuntu
+ fonts-ubuntu
+ 0.83-6ubuntu1
+ fonts
+ 0.83-6ubuntu1
+
+
+ all
+ 45
+ deb
+ friendly-recovery
+ Ubuntu
+
+ 0.2.42
+ admin
+ 0.2.42
+
+
+ all
+ 26
+ deb
+ ftp
+ Ubuntu
+ tnftp
+ 20210827-4build1
+ oldlibs
+ 20210827-4build1
+
+
+ amd64
+ 90
+ deb
+ fuse3
+ Ubuntu
+
+ 3.10.5-1build1
+ utils
+ 3.10.5-1build1
+
+
+ amd64
+ 6648
+ deb
+ fwupd
+ Ubuntu
+
+ 1.7.5-3
+ admin
+ 1.7.5-3
+
+
+ amd64
+ 77
+ deb
+ fwupd-signed
+ Ubuntu
+ fwupd-signed (1.44)
+ 1.44
+ utils
+ 1.44+1.2-3
+
+
+ amd64
+ 1680
+ deb
+ gawk
+ Ubuntu
+
+ 1:5.1.0-1build3
+ interpreters
+ 1:5.1.0-1build3
+
+
+ amd64
+ 266
+ deb
+ gcc-12-base
+ Ubuntu
+ gcc-12
+ 12-20220319-1ubuntu1
+ libs
+ 12-20220319-1ubuntu1
+
+
+ amd64
+ 726
+ deb
+ gdisk
+ Ubuntu
+
+ 1.0.8-4build1
+ admin
+ 1.0.8-4build1
+
+
+ amd64
+ 284
+ deb
+ gettext-base
+ Ubuntu
+ gettext
+ 0.21-4ubuntu4
+ utils
+ 0.21-4ubuntu4
+
+
+ amd64
+ 677
+ deb
+ gir1.2-glib-2.0
+ Ubuntu
+ gobject-introspection
+ 1.72.0-1
+ introspection
+ 1.72.0-1
+
+
+ amd64
+ 123
+ deb
+ gir1.2-packagekitglib-1.0
+ Ubuntu
+ packagekit
+ 1.2.5-2ubuntu2
+ introspection
+ 1.2.5-2ubuntu2
+
+
+ amd64
+ 18348
+ deb
+ git
+ Ubuntu
+
+ 1:2.34.1-1ubuntu1.8
+ vcs
+ 1:2.34.1-1ubuntu1.8
+
+
+ all
+ 1957
+ deb
+ git-man
+ Ubuntu
+ git
+ 1:2.34.1-1ubuntu1.4
+ doc
+ 1:2.34.1-1ubuntu1.4
+
+
+ all
+ 473
+ deb
+ gnupg
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ all
+ 392
+ deb
+ gnupg-l10n
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ localization
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 787
+ deb
+ gnupg-utils
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 1121
+ deb
+ gpg
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 595
+ deb
+ gpg-agent
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 184
+ deb
+ gpg-wks-client
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 168
+ deb
+ gpg-wks-server
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 280
+ deb
+ gpgconf
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 480
+ deb
+ gpgsm
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 324
+ deb
+ gpgv
+ Ubuntu
+ gnupg2
+ 2.2.27-3ubuntu2.1
+ utils
+ 2.2.27-3ubuntu2.1
+
+
+ amd64
+ 496
+ deb
+ grep
+ Ubuntu
+
+ 3.7-1build1
+ utils
+ 3.7-1build1
+
+
+ amd64
+ 3444
+ deb
+ groff-base
+ Ubuntu
+ groff
+ 1.22.4-8build1
+ text
+ 1.22.4-8build1
+
+
+ amd64
+ 13836
+ deb
+ grub-common
+ Ubuntu
+ grub2
+ 2.06-2ubuntu7
+ admin
+ 2.06-2ubuntu7
+
+
+ amd64
+ 47
+ deb
+ grub-gfxpayload-lists
+ Ubuntu
+
+ 0.7
+ admin
+ 0.7
+
+
+ amd64
+ 561
+ deb
+ grub-pc
+ Ubuntu
+ grub2
+ 2.06-2ubuntu7
+ admin
+ 2.06-2ubuntu7
+
+
+ amd64
+ 3108
+ deb
+ grub-pc-bin
+ Ubuntu
+ grub2
+ 2.06-2ubuntu7
+ admin
+ 2.06-2ubuntu7
+
+
+ amd64
+ 1354
+ deb
+ grub2-common
+ Ubuntu
+ grub2
+ 2.06-2ubuntu7
+ admin
+ 2.06-2ubuntu7
+
+
+ amd64
+ 244
+ deb
+ gzip
+ Ubuntu
+
+ 1.10-4ubuntu4
+ utils
+ 1.10-4ubuntu4
+
+
+ amd64
+ 244
+ deb
+ hdparm
+ Ubuntu
+
+ 9.60+ds-1build3
+ admin
+ 9.60+ds-1build3
+
+
+ amd64
+ 51
+ deb
+ hostname
+ Ubuntu
+
+ 3.23ubuntu2
+ admin
+ 3.23ubuntu2
+
+
+ amd64
+ 334
+ deb
+ htop
+ Ubuntu
+
+ 3.0.5-7build2
+ utils
+ 3.0.5-7build2
+
+
+ amd64
+ 849
+ deb
+ info
+ Ubuntu
+ texinfo
+ 6.8-4build1
+ doc
+ 6.8-4build1
+
+
+ amd64
+ 22
+ deb
+ init
+ Ubuntu
+ init-system-helpers
+ 1.62
+ metapackages
+ 1.62
+
+
+ all
+ 133
+ deb
+ init-system-helpers
+ Ubuntu
+
+ 1.62
+ admin
+ 1.62
+
+
+ all
+ 147
+ deb
+ initramfs-tools
+ Ubuntu
+
+ 0.140ubuntu13
+ utils
+ 0.140ubuntu13
+
+
+ amd64
+ 135
+ deb
+ initramfs-tools-bin
+ Ubuntu
+ initramfs-tools
+ 0.140ubuntu13
+ utils
+ 0.140ubuntu13
+
+
+ all
+ 274
+ deb
+ initramfs-tools-core
+ Ubuntu
+ initramfs-tools
+ 0.140ubuntu13
+ utils
+ 0.140ubuntu13
+
+
+ amd64
+ 257
+ deb
+ install-info
+ Ubuntu
+ texinfo
+ 6.8-4build1
+ doc
+ 6.8-4build1
+
+
+ amd64
+ 2880
+ deb
+ iproute2
+ Ubuntu
+
+ 5.15.0-1ubuntu2
+ net
+ 5.15.0-1ubuntu2
+
+
+ amd64
+ 2837
+ deb
+ iptables
+ Ubuntu
+
+ 1.8.7-1ubuntu5
+ net
+ 1.8.7-1ubuntu5
+
+
+ amd64
+ 113
+ deb
+ iputils-ping
+ Ubuntu
+ iputils
+ 3:20211215-1
+ net
+ 3:20211215-1
+
+
+ amd64
+ 52
+ deb
+ iputils-tracepath
+ Ubuntu
+ iputils
+ 3:20211215-1
+ net
+ 3:20211215-1
+
+
+ amd64
+ 148
+ deb
+ irqbalance
+ Ubuntu
+
+ 1.8.0-1build1
+ utils
+ 1.8.0-1build1
+
+
+ amd64
+ 671
+ deb
+ isc-dhcp-client
+ Ubuntu
+ isc-dhcp
+ 4.4.1-2.3ubuntu2.1
+ net
+ 4.4.1-2.3ubuntu2.1
+
+
+ amd64
+ 164
+ deb
+ isc-dhcp-common
+ Ubuntu
+ isc-dhcp
+ 4.4.1-2.3ubuntu2.1
+ net
+ 4.4.1-2.3ubuntu2.1
+
+
+ all
+ 19769
+ deb
+ iso-codes
+ Ubuntu
+
+ 4.9.0-1
+ misc
+ 4.9.0-1
+
+
+ amd64
+ 100
+ deb
+ jq
+ Ubuntu
+
+ 1.6-2.1ubuntu3
+ utils
+ 1.6-2.1ubuntu3
+
+
+ amd64
+ 1328
+ deb
+ kbd
+ Ubuntu
+
+ 2.3.0-3ubuntu4
+ utils
+ 2.3.0-3ubuntu4
+
+
+ all
+ 842
+ deb
+ keyboard-configuration
+ Ubuntu
+ console-setup
+ 1.205ubuntu3
+ utils
+ 1.205ubuntu3
+
+
+ amd64
+ 547
+ deb
+ klibc-utils
+ Ubuntu
+ klibc
+ 2.0.10-4
+ libs
+ 2.0.10-4
+
+
+ amd64
+ 251
+ deb
+ kmod
+ Ubuntu
+
+ 29-1ubuntu1
+ admin
+ 29-1ubuntu1
+
+
+ amd64
+ 95
+ deb
+ kpartx
+ Ubuntu
+ multipath-tools
+ 0.8.8-1ubuntu1
+ admin
+ 0.8.8-1ubuntu1
+
+
+ amd64
+ 402
+ deb
+ landscape-common
+ Ubuntu
+ landscape-client
+ 19.12-0ubuntu13
+ admin
+ 19.12-0ubuntu13
+
+
+ amd64
+ 694
+ deb
+ ldap-utils
+ Ubuntu
+ openldap
+ 2.5.14+dfsg-0ubuntu0.22.04.1
+ net
+ 2.5.14+dfsg-0ubuntu0.22.04.1
+
+
+ all
+ 161
+ deb
+ ldapscripts
+ Ubuntu
+
+ 2.0.8-1ubuntu2
+ admin
+ 2.0.8-1ubuntu2
+
+
+ amd64
+ 321
+ deb
+ less
+ Ubuntu
+
+ 590-1ubuntu0.22.04.1
+ text
+ 590-1ubuntu0.22.04.1
+
+
+ amd64
+ 67
+ deb
+ libacl1
+ Ubuntu
+ acl
+ 2.3.1-1
+ libs
+ 2.3.1-1
+
+
+ amd64
+ 37
+ deb
+ libaio1
+ Ubuntu
+ libaio
+ 0.3.112-13build1
+ libs
+ 0.3.112-13build1
+
+
+ amd64
+ 170
+ deb
+ libapparmor1
+ Ubuntu
+ apparmor
+ 3.0.4-2ubuntu2.1
+ libs
+ 3.0.4-2ubuntu2.1
+
+
+ amd64
+ 576
+ deb
+ libappstream4
+ Ubuntu
+ appstream
+ 0.15.2-2
+ libs
+ 0.15.2-2
+
+
+ amd64
+ 3173
+ deb
+ libapt-pkg6.0
+ Ubuntu
+ apt
+ 2.4.5
+ libs
+ 2.4.5
+
+
+ amd64
+ 876
+ deb
+ libarchive13
+ Ubuntu
+ libarchive
+ 3.6.0-1ubuntu1
+ libs
+ 3.6.0-1ubuntu1
+
+
+ amd64
+ 56
+ deb
+ libargon2-1
+ Ubuntu
+ argon2
+ 0~20171227-0.3
+ libs
+ 0~20171227-0.3
+
+
+ amd64
+ 110
+ deb
+ libassuan0
+ Ubuntu
+ libassuan
+ 2.5.5-1build1
+ libs
+ 2.5.5-1build1
+
+
+ amd64
+ 82
+ deb
+ libatasmart4
+ Ubuntu
+ libatasmart
+ 0.19-5build2
+ libs
+ 0.19-5build2
+
+
+ amd64
+ 110
+ deb
+ libatm1
+ Ubuntu
+ linux-atm
+ 1:2.5.1-4build2
+ libs
+ 1:2.5.1-4build2
+
+
+ amd64
+ 57
+ deb
+ libattr1
+ Ubuntu
+ attr
+ 1:2.5.1-1build1
+ libs
+ 1:2.5.1-1build1
+
+
+ all
+ 23
+ deb
+ libaudit-common
+ Ubuntu
+ audit
+ 1:3.0.7-1build1
+ libs
+ 1:3.0.7-1build1
+
+
+ amd64
+ 156
+ deb
+ libaudit1
+ Ubuntu
+ audit
+ 1:3.0.7-1build1
+ libs
+ 1:3.0.7-1build1
+
+
+ amd64
+ 477
+ deb
+ libaugeas0
+ Ubuntu
+ augeas
+ 1.13.0-1
+ libs
+ 1.13.0-1
+
+
+ amd64
+ 2776
+ deb
+ libbinutils
+ Ubuntu
+ binutils
+ 2.38-4ubuntu2.1
+ devel
+ 2.38-4ubuntu2.1
+
+
+ amd64
+ 323
+ deb
+ libblkid1
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ libs
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 71
+ deb
+ libblockdev-crypto2
+ Ubuntu
+ libblockdev
+ 2.26-1
+ libs
+ 2.26-1
+
+
+ amd64
+ 81
+ deb
+ libblockdev-fs2
+ Ubuntu
+ libblockdev
+ 2.26-1
+ libs
+ 2.26-1
+
+
+ amd64
+ 34
+ deb
+ libblockdev-loop2
+ Ubuntu
+ libblockdev
+ 2.26-1
+ libs
+ 2.26-1
+
+
+ amd64
+ 35
+ deb
+ libblockdev-part-err2
+ Ubuntu
+ libblockdev
+ 2.26-1
+ libs
+ 2.26-1
+
+
+ amd64
+ 64
+ deb
+ libblockdev-part2
+ Ubuntu
+ libblockdev
+ 2.26-1
+ libs
+ 2.26-1
+
+
+ amd64
+ 43
+ deb
+ libblockdev-swap2
+ Ubuntu
+ libblockdev
+ 2.26-1
+ libs
+ 2.26-1
+
+
+ amd64
+ 60
+ deb
+ libblockdev-utils2
+ Ubuntu
+ libblockdev
+ 2.26-1
+ libs
+ 2.26-1
+
+
+ amd64
+ 224
+ deb
+ libblockdev2
+ Ubuntu
+ libblockdev
+ 2.26-1
+ libs
+ 2.26-1
+
+
+ amd64
+ 344
+ deb
+ libbpf0
+ Ubuntu
+ libbpf (0.5.0-1)
+ 0.5.0-1
+ libs
+ 1:0.5.0-1
+
+
+ amd64
+ 784
+ deb
+ libbrotli1
+ Ubuntu
+ brotli
+ 1.0.9-2build6
+ libs
+ 1.0.9-2build6
+
+
+ amd64
+ 136
+ deb
+ libbsd0
+ Ubuntu
+ libbsd
+ 0.11.5-1
+ libs
+ 0.11.5-1
+
+
+ amd64
+ 100
+ deb
+ libbz2-1.0
+ Ubuntu
+ bzip2
+ 1.0.8-5build1
+ libs
+ 1.0.8-5build1
+
+
+ amd64
+ 2537
+ deb
+ libc-bin
+ Ubuntu
+ glibc
+ 2.35-0ubuntu3
+ libs
+ 2.35-0ubuntu3
+
+
+ amd64
+ 13592
+ deb
+ libc6
+ Ubuntu
+ glibc
+ 2.35-0ubuntu3
+ libs
+ 2.35-0ubuntu3
+
+
+ amd64
+ 45
+ deb
+ libcap-ng0
+ Ubuntu
+ libcap-ng
+ 0.7.9-2.2build3
+ libs
+ 0.7.9-2.2build3
+
+
+ amd64
+ 65
+ deb
+ libcap2
+ Ubuntu
+
+ 1:2.44-1build3
+ libs
+ 1:2.44-1build3
+
+
+ amd64
+ 115
+ deb
+ libcap2-bin
+ Ubuntu
+ libcap2
+ 1:2.44-1build3
+ utils
+ 1:2.44-1build3
+
+
+ amd64
+ 83
+ deb
+ libcbor0.8
+ Ubuntu
+ libcbor
+ 0.8.0-2ubuntu1
+ libs
+ 0.8.0-2ubuntu1
+
+
+ amd64
+ 101
+ deb
+ libcom-err2
+ Ubuntu
+ e2fsprogs
+ 1.46.5-2ubuntu1.1
+ libs
+ 1.46.5-2ubuntu1.1
+
+
+ amd64
+ 225
+ deb
+ libcrypt1
+ Ubuntu
+ libxcrypt
+ 1:4.4.27-1
+ libs
+ 1:4.4.27-1
+
+
+ amd64
+ 576
+ deb
+ libcryptsetup12
+ Ubuntu
+ cryptsetup
+ 2:2.4.3-1ubuntu1
+ libs
+ 2:2.4.3-1ubuntu1
+
+
+ amd64
+ 311
+ deb
+ libctf-nobfd0
+ Ubuntu
+ binutils
+ 2.38-3ubuntu1
+ devel
+ 2.38-3ubuntu1
+
+
+ amd64
+ 239
+ deb
+ libctf0
+ Ubuntu
+ binutils
+ 2.38-4ubuntu2.1
+ devel
+ 2.38-4ubuntu2.1
+
+
+ amd64
+ 766
+ deb
+ libcurl3-gnutls
+ Ubuntu
+ curl
+ 7.81.0-1ubuntu1.3
+ libs
+ 7.81.0-1ubuntu1.3
+
+
+ amd64
+ 787
+ deb
+ libcurl4
+ Ubuntu
+ curl
+ 7.81.0-1ubuntu1.10
+ libs
+ 7.81.0-1ubuntu1.10
+
+
+ amd64
+ 1750
+ deb
+ libdb5.3
+ Ubuntu
+ db5.3
+ 5.3.28+dfsg1-0.8ubuntu3
+ libs
+ 5.3.28+dfsg1-0.8ubuntu3
+
+
+ amd64
+ 457
+ deb
+ libdbus-1-3
+ Ubuntu
+ dbus
+ 1.12.20-2ubuntu4
+ libs
+ 1.12.20-2ubuntu4
+
+
+ amd64
+ 79
+ deb
+ libdebconfclient0
+ Ubuntu
+ cdebconf
+ 0.261ubuntu1
+ libs
+ 0.261ubuntu1
+
+
+ amd64
+ 76
+ deb
+ libdevmapper-event1.02.1
+ Ubuntu
+ lvm2 (2.03.11-2.1ubuntu4)
+ 2.03.11-2.1ubuntu4
+ libs
+ 2:1.02.175-2.1ubuntu4
+
+
+ amd64
+ 492
+ deb
+ libdevmapper1.02.1
+ Ubuntu
+ lvm2 (2.03.11-2.1ubuntu4)
+ 2.03.11-2.1ubuntu4
+ libs
+ 2:1.02.175-2.1ubuntu4
+
+
+ amd64
+ 2262
+ deb
+ libdns-export1110
+ Ubuntu
+ bind9-libs
+ 1:9.11.19+dfsg-2.1ubuntu3
+ libs
+ 1:9.11.19+dfsg-2.1ubuntu3
+
+
+ all
+ 45
+ deb
+ libdrm-common
+ Ubuntu
+ libdrm
+ 2.4.110-1ubuntu1
+ libs
+ 2.4.110-1ubuntu1
+
+
+ amd64
+ 128
+ deb
+ libdrm2
+ Ubuntu
+ libdrm
+ 2.4.110-1ubuntu1
+ libs
+ 2.4.110-1ubuntu1
+
+
+ amd64
+ 729
+ deb
+ libdw1
+ Ubuntu
+ elfutils
+ 0.186-1build1
+ libs
+ 0.186-1build1
+
+
+ amd64
+ 39
+ deb
+ libeatmydata1
+ Ubuntu
+ libeatmydata
+ 130-2build1
+ libs
+ 130-2build1
+
+
+ amd64
+ 260
+ deb
+ libedit2
+ Ubuntu
+ libedit
+ 3.1-20210910-1build1
+ libs
+ 3.1-20210910-1build1
+
+
+ amd64
+ 123
+ deb
+ libefiboot1
+ Ubuntu
+ efivar
+ 37-6ubuntu2
+ libs
+ 37-6ubuntu2
+
+
+ amd64
+ 171
+ deb
+ libefivar1
+ Ubuntu
+ efivar
+ 37-6ubuntu2
+ libs
+ 37-6ubuntu2
+
+
+ amd64
+ 192
+ deb
+ libelf1
+ Ubuntu
+ elfutils
+ 0.186-1build1
+ libs
+ 0.186-1build1
+
+
+ all
+ 71
+ deb
+ liberror-perl
+ Ubuntu
+
+ 0.17029-1
+ perl
+ 0.17029-1
+
+
+ amd64
+ 31
+ deb
+ libestr0
+ Ubuntu
+ libestr
+ 0.1.10-2.1build3
+ libs
+ 0.1.10-2.1build3
+
+
+ amd64
+ 256
+ deb
+ libevent-core-2.1-7
+ Ubuntu
+ libevent
+ 2.1.12-stable-1build3
+ libs
+ 2.1.12-stable-1build3
+
+
+ amd64
+ 432
+ deb
+ libexpat1
+ Ubuntu
+ expat
+ 2.4.7-1
+ libs
+ 2.4.7-1
+
+
+ amd64
+ 574
+ deb
+ libext2fs2
+ Ubuntu
+ e2fsprogs
+ 1.46.5-2ubuntu1.1
+ libs
+ 1.46.5-2ubuntu1.1
+
+
+ amd64
+ 69
+ deb
+ libfastjson4
+ Ubuntu
+ libfastjson
+ 0.99.9-1build2
+ libs
+ 0.99.9-1build2
+
+
+ amd64
+ 433
+ deb
+ libfdisk1
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ libs
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 69
+ deb
+ libffi8
+ Ubuntu
+ libffi
+ 3.4.2-4
+ libs
+ 3.4.2-4
+
+
+ amd64
+ 236
+ deb
+ libfido2-1
+ Ubuntu
+ libfido2
+ 1.10.0-1
+ libs
+ 1.10.0-1
+
+
+ amd64
+ 869
+ deb
+ libflashrom1
+ Ubuntu
+ flashrom
+ 1.2-5build1
+ libs
+ 1.2-5build1
+
+
+ amd64
+ 870
+ deb
+ libfreetype6
+ Ubuntu
+ freetype
+ 2.11.1+dfsg-1build1
+ libs
+ 2.11.1+dfsg-1build1
+
+
+ amd64
+ 136
+ deb
+ libfribidi0
+ Ubuntu
+ fribidi
+ 1.0.8-2ubuntu3.1
+ libs
+ 1.0.8-2ubuntu3.1
+
+
+ amd64
+ 81
+ deb
+ libftdi1-2
+ Ubuntu
+ libftdi1
+ 1.5-5build3
+ libs
+ 1.5-5build3
+
+
+ amd64
+ 282
+ deb
+ libfuse3-3
+ Ubuntu
+ fuse3
+ 3.10.5-1build1
+ libs
+ 3.10.5-1build1
+
+
+ amd64
+ 389
+ deb
+ libfwupd2
+ Ubuntu
+ fwupd
+ 1.7.5-3
+ libs
+ 1.7.5-3
+
+
+ amd64
+ 588
+ deb
+ libfwupdplugin5
+ Ubuntu
+ fwupd
+ 1.7.5-3
+ libs
+ 1.7.5-3
+
+
+ amd64
+ 90
+ deb
+ libgcab-1.0-0
+ Ubuntu
+ gcab
+ 1.4-3build2
+ libs
+ 1.4-3build2
+
+
+ amd64
+ 140
+ deb
+ libgcc-s1
+ Ubuntu
+ gcc-12
+ 12-20220319-1ubuntu1
+ libs
+ 12-20220319-1ubuntu1
+
+
+ amd64
+ 1354
+ deb
+ libgcrypt20
+ Ubuntu
+
+ 1.9.4-3ubuntu3
+ libs
+ 1.9.4-3ubuntu3
+
+
+ amd64
+ 45
+ deb
+ libgdbm-compat4
+ Ubuntu
+ gdbm
+ 1.23-1
+ libs
+ 1.23-1
+
+
+ amd64
+ 100
+ deb
+ libgdbm6
+ Ubuntu
+ gdbm
+ 1.23-1
+ libs
+ 1.23-1
+
+
+ amd64
+ 175
+ deb
+ libgirepository-1.0-1
+ Ubuntu
+ gobject-introspection
+ 1.72.0-1
+ libs
+ 1.72.0-1
+
+
+ amd64
+ 4076
+ deb
+ libglib2.0-0
+ Ubuntu
+ glib2.0
+ 2.72.1-1
+ libs
+ 2.72.1-1
+
+
+ amd64
+ 341
+ deb
+ libglib2.0-bin
+ Ubuntu
+ glib2.0
+ 2.72.1-1
+ misc
+ 2.72.1-1
+
+
+ all
+ 112
+ deb
+ libglib2.0-data
+ Ubuntu
+ glib2.0
+ 2.72.1-1
+ libs
+ 2.72.1-1
+
+
+ amd64
+ 544
+ deb
+ libgmp10
+ Ubuntu
+ gmp
+ 2:6.2.1+dfsg-3ubuntu1
+ libs
+ 2:6.2.1+dfsg-3ubuntu1
+
+
+ amd64
+ 2284
+ deb
+ libgnutls30
+ Ubuntu
+ gnutls28
+ 3.7.3-4ubuntu1
+ libs
+ 3.7.3-4ubuntu1
+
+
+ amd64
+ 189
+ deb
+ libgpg-error0
+ Ubuntu
+ libgpg-error
+ 1.43-3
+ libs
+ 1.43-3
+
+
+ amd64
+ 372
+ deb
+ libgpgme11
+ Ubuntu
+ gpgme1.0
+ 1.16.0-1.2ubuntu4
+ libs
+ 1.16.0-1.2ubuntu4
+
+
+ amd64
+ 65
+ deb
+ libgpm2
+ Ubuntu
+ gpm
+ 1.20.7-10build1
+ libs
+ 1.20.7-10build1
+
+
+ amd64
+ 455
+ deb
+ libgssapi-krb5-2
+ Ubuntu
+ krb5
+ 1.19.2-2
+ libs
+ 1.19.2-2
+
+
+ amd64
+ 2984
+ deb
+ libgstreamer1.0-0
+ Ubuntu
+ gstreamer1.0
+ 1.20.1-1
+ libs
+ 1.20.1-1
+
+
+ amd64
+ 69
+ deb
+ libgudev-1.0-0
+ Ubuntu
+ libgudev (237-2build1)
+ 237-2build1
+ libs
+ 1:237-2build1
+
+
+ amd64
+ 97
+ deb
+ libgusb2
+ Ubuntu
+ libgusb
+ 0.3.10-1
+ libs
+ 0.3.10-1
+
+
+ amd64
+ 336
+ deb
+ libhogweed6
+ Ubuntu
+ nettle
+ 3.7.3-1build2
+ libs
+ 3.7.3-1build2
+
+
+ amd64
+ 34444
+ deb
+ libicu70
+ Ubuntu
+ icu
+ 70.1-2
+ libs
+ 70.1-2
+
+
+ amd64
+ 220
+ deb
+ libidn2-0
+ Ubuntu
+ libidn2
+ 2.3.2-2build1
+ libs
+ 2.3.2-2build1
+
+
+ amd64
+ 30
+ deb
+ libinih1
+ Ubuntu
+ libinih
+ 53-1ubuntu3
+ libs
+ 53-1ubuntu3
+
+
+ all
+ 4321
+ deb
+ libintl-perl
+ Ubuntu
+
+ 1.26-3build2
+ perl
+ 1.26-3build2
+
+
+ amd64
+ 53
+ deb
+ libintl-xs-perl
+ Ubuntu
+ libintl-perl
+ 1.26-3build2
+ perl
+ 1.26-3build2
+
+
+ amd64
+ 83
+ deb
+ libip4tc2
+ Ubuntu
+ iptables
+ 1.8.7-1ubuntu5
+ libs
+ 1.8.7-1ubuntu5
+
+
+ amd64
+ 83
+ deb
+ libip6tc2
+ Ubuntu
+ iptables
+ 1.8.7-1ubuntu5
+ libs
+ 1.8.7-1ubuntu5
+
+
+ amd64
+ 510
+ deb
+ libisc-export1105
+ Ubuntu
+ bind9-libs
+ 1:9.11.19+dfsg-2.1ubuntu3
+ libs
+ 1:9.11.19+dfsg-2.1ubuntu3
+
+
+ amd64
+ 492
+ deb
+ libisns0
+ Ubuntu
+ open-isns
+ 0.101-0ubuntu2
+ libs
+ 0.101-0ubuntu2
+
+
+ amd64
+ 91
+ deb
+ libjansson4
+ Ubuntu
+ jansson
+ 2.13.1-1.1build3
+ libs
+ 2.13.1-1.1build3
+
+
+ amd64
+ 96
+ deb
+ libjcat1
+ Ubuntu
+ libjcat
+ 0.1.9-1
+ libs
+ 0.1.9-1
+
+
+ amd64
+ 347
+ deb
+ libjq1
+ Ubuntu
+ jq
+ 1.6-2.1ubuntu3
+ utils
+ 1.6-2.1ubuntu3
+
+
+ amd64
+ 97
+ deb
+ libjson-c5
+ Ubuntu
+ json-c
+ 0.15-3~ubuntu1.22.04.1
+ libs
+ 0.15-3~ubuntu1.22.04.1
+
+
+ amd64
+ 210
+ deb
+ libjson-glib-1.0-0
+ Ubuntu
+ json-glib
+ 1.6.6-1build1
+ libs
+ 1.6.6-1build1
+
+
+ all
+ 44
+ deb
+ libjson-glib-1.0-common
+ Ubuntu
+ json-glib
+ 1.6.6-1build1
+ libs
+ 1.6.6-1build1
+
+
+ amd64
+ 292
+ deb
+ libk5crypto3
+ Ubuntu
+ krb5
+ 1.19.2-2
+ libs
+ 1.19.2-2
+
+
+ amd64
+ 47
+ deb
+ libkeyutils1
+ Ubuntu
+ keyutils
+ 1.6.1-2ubuntu3
+ misc
+ 1.6.1-2ubuntu3
+
+
+ amd64
+ 114
+ deb
+ libklibc
+ Ubuntu
+ klibc
+ 2.0.10-4
+ libs
+ 2.0.10-4
+
+
+ amd64
+ 139
+ deb
+ libkmod2
+ Ubuntu
+ kmod
+ 29-1ubuntu1
+ libs
+ 29-1ubuntu1
+
+
+ amd64
+ 1052
+ deb
+ libkrb5-3
+ Ubuntu
+ krb5
+ 1.19.2-2
+ libs
+ 1.19.2-2
+
+
+ amd64
+ 164
+ deb
+ libkrb5support0
+ Ubuntu
+ krb5
+ 1.19.2-2
+ libs
+ 1.19.2-2
+
+
+ amd64
+ 302
+ deb
+ libksba8
+ Ubuntu
+ libksba
+ 1.6.0-2build1
+ libs
+ 1.6.0-2build1
+
+
+ amd64
+ 565
+ deb
+ libldap-2.5-0
+ Ubuntu
+ openldap
+ 2.5.14+dfsg-0ubuntu0.22.04.1
+ libs
+ 2.5.14+dfsg-0ubuntu0.22.04.1
+
+
+ all
+ 108
+ deb
+ libldap-common
+ Ubuntu
+ openldap
+ 2.5.12+dfsg-0ubuntu0.22.04.1
+ libs
+ 2.5.12+dfsg-0ubuntu0.22.04.1
+
+
+ amd64
+ 109
+ deb
+ liblmdb0
+ Ubuntu
+ lmdb
+ 0.9.24-1build2
+ libs
+ 0.9.24-1build2
+
+
+ amd64
+ 59
+ deb
+ liblocale-gettext-perl
+ Ubuntu
+
+ 1.07-4build3
+ perl
+ 1.07-4build3
+
+
+ amd64
+ 419
+ deb
+ libltdl7
+ Ubuntu
+ libtool
+ 2.4.6-15build2
+ libs
+ 2.4.6-15build2
+
+
+ amd64
+ 2938
+ deb
+ liblvm2cmd2.03
+ Ubuntu
+ lvm2
+ 2.03.11-2.1ubuntu4
+ libs
+ 2.03.11-2.1ubuntu4
+
+
+ amd64
+ 145
+ deb
+ liblz4-1
+ Ubuntu
+ lz4
+ 1.9.3-2build2
+ libs
+ 1.9.3-2build2
+
+
+ amd64
+ 290
+ deb
+ liblzma5
+ Ubuntu
+ xz-utils
+ 5.2.5-2ubuntu1
+ libs
+ 5.2.5-2ubuntu1
+
+
+ amd64
+ 159
+ deb
+ liblzo2-2
+ Ubuntu
+ lzo2
+ 2.10-2build3
+ libs
+ 2.10-2build3
+
+
+ amd64
+ 7127
+ deb
+ libmagic-mgc
+ Ubuntu
+ file
+ 1:5.41-3
+ libs
+ 1:5.41-3
+
+
+ amd64
+ 228
+ deb
+ libmagic1
+ Ubuntu
+ file
+ 1:5.41-3
+ libs
+ 1:5.41-3
+
+
+ amd64
+ 76
+ deb
+ libmaxminddb0
+ Ubuntu
+ libmaxminddb
+ 1.5.2-1build2
+ libs
+ 1.5.2-1build2
+
+
+ amd64
+ 492
+ deb
+ libmbim-glib4
+ Ubuntu
+ libmbim
+ 1.26.2-1build1
+ libs
+ 1.26.2-1build1
+
+
+ amd64
+ 33
+ deb
+ libmbim-proxy
+ Ubuntu
+ libmbim
+ 1.26.2-1build1
+ net
+ 1.26.2-1build1
+
+
+ amd64
+ 71
+ deb
+ libmd0
+ Ubuntu
+ libmd
+ 1.0.4-1build1
+ libs
+ 1.0.4-1build1
+
+
+ amd64
+ 1122
+ deb
+ libmm-glib0
+ Ubuntu
+ modemmanager
+ 1.18.6-1
+ libs
+ 1.18.6-1
+
+
+ amd64
+ 47
+ deb
+ libmnl0
+ Ubuntu
+ libmnl
+ 1.0.4-3build2
+ libs
+ 1.0.4-3build2
+
+
+ all
+ 29
+ deb
+ libmodule-find-perl
+ Ubuntu
+
+ 0.15-1
+ perl
+ 0.15-1
+
+
+ all
+ 95
+ deb
+ libmodule-scandeps-perl
+ Ubuntu
+
+ 1.31-1
+ perl
+ 1.31-1
+
+
+ amd64
+ 382
+ deb
+ libmount1
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ libs
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 250
+ deb
+ libmpdec3
+ Ubuntu
+ mpdecimal
+ 2.5.1-2build2
+ libs
+ 2.5.1-2build2
+
+
+ amd64
+ 3405
+ deb
+ libmpfr6
+ Ubuntu
+ mpfr4
+ 4.1.0-3build3
+ libs
+ 4.1.0-3build3
+
+
+ amd64
+ 96
+ deb
+ libmspack0
+ Ubuntu
+ libmspack
+ 0.10.1-2build2
+ libs
+ 0.10.1-2build2
+
+
+ amd64
+ 329
+ deb
+ libncurses6
+ Ubuntu
+ ncurses
+ 6.3-2
+ libs
+ 6.3-2
+
+
+ amd64
+ 422
+ deb
+ libncursesw6
+ Ubuntu
+ ncurses
+ 6.3-2
+ libs
+ 6.3-2
+
+
+ amd64
+ 141
+ deb
+ libnetfilter-conntrack3
+ Ubuntu
+ libnetfilter-conntrack
+ 1.0.9-1
+ libs
+ 1.0.9-1
+
+
+ amd64
+ 280
+ deb
+ libnetplan0
+ Ubuntu
+ netplan.io
+ 0.104-0ubuntu2
+ libs
+ 0.104-0ubuntu2
+
+
+ amd64
+ 356
+ deb
+ libnettle8
+ Ubuntu
+ nettle
+ 3.7.3-1build2
+ libs
+ 3.7.3-1build2
+
+
+ amd64
+ 200
+ deb
+ libnewt0.52
+ Ubuntu
+ newt
+ 0.52.21-5ubuntu2
+ libs
+ 0.52.21-5ubuntu2
+
+
+ amd64
+ 48
+ deb
+ libnfnetlink0
+ Ubuntu
+ libnfnetlink
+ 1.0.1-3build3
+ libs
+ 1.0.1-3build3
+
+
+ amd64
+ 913
+ deb
+ libnftables1
+ Ubuntu
+ nftables
+ 1.0.2-1ubuntu2
+ libs
+ 1.0.2-1ubuntu2
+
+
+ amd64
+ 227
+ deb
+ libnftnl11
+ Ubuntu
+ libnftnl
+ 1.2.1-1build1
+ libs
+ 1.2.1-1build1
+
+
+ amd64
+ 203
+ deb
+ libnghttp2-14
+ Ubuntu
+ nghttp2
+ 1.43.0-1build3
+ libs
+ 1.43.0-1build3
+
+
+ amd64
+ 180
+ deb
+ libnl-3-200
+ Ubuntu
+ libnl3
+ 3.5.0-0.1
+ libs
+ 3.5.0-0.1
+
+
+ amd64
+ 61
+ deb
+ libnl-genl-3-200
+ Ubuntu
+ libnl3
+ 3.5.0-0.1
+ libs
+ 3.5.0-0.1
+
+
+ amd64
+ 40
+ deb
+ libnpth0
+ Ubuntu
+ npth
+ 1.6-3build2
+ libs
+ 1.6-3build2
+
+
+ amd64
+ 123
+ deb
+ libnsl2
+ Ubuntu
+ libnsl
+ 1.3.0-2build2
+ libs
+ 1.3.0-2build2
+
+
+ amd64
+ 314
+ deb
+ libnspr4
+ Ubuntu
+ nspr
+ 2:4.32-3build1
+ libs
+ 2:4.32-3build1
+
+
+ amd64
+ 488
+ deb
+ libnss-systemd
+ Ubuntu
+ systemd
+ 249.11-0ubuntu3.4
+ admin
+ 249.11-0ubuntu3.4
+
+
+ amd64
+ 3804
+ deb
+ libnss3
+ Ubuntu
+ nss
+ 2:3.68.2-0ubuntu1.1
+ libs
+ 2:3.68.2-0ubuntu1.1
+
+
+ amd64
+ 371
+ deb
+ libntfs-3g89
+ Ubuntu
+ ntfs-3g
+ 1:2021.8.22-3ubuntu1.1
+ libs
+ 1:2021.8.22-3ubuntu1.1
+
+
+ amd64
+ 71
+ deb
+ libnuma1
+ Ubuntu
+ numactl
+ 2.0.14-3ubuntu2
+ libs
+ 2.0.14-3ubuntu2
+
+
+ amd64
+ 615
+ deb
+ libonig5
+ Ubuntu
+ libonig
+ 6.9.7.1-2build1
+ libs
+ 6.9.7.1-2build1
+
+
+ amd64
+ 198
+ deb
+ libopeniscsiusr
+ Ubuntu
+ open-iscsi
+ 2.1.5-1ubuntu1
+ net
+ 2.1.5-1ubuntu1
+
+
+ amd64
+ 1292
+ deb
+ libp11-kit0
+ Ubuntu
+ p11-kit
+ 0.24.0-6build1
+ libs
+ 0.24.0-6build1
+
+
+ amd64
+ 463
+ deb
+ libpackagekit-glib2-18
+ Ubuntu
+ packagekit
+ 1.2.5-2ubuntu2
+ libs
+ 1.2.5-2ubuntu2
+
+
+ amd64
+ 45
+ deb
+ libpam-cap
+ Ubuntu
+ libcap2
+ 1:2.44-1build3
+ admin
+ 1:2.44-1build3
+
+
+ amd64
+ 1138
+ deb
+ libpam-modules
+ Ubuntu
+ pam
+ 1.4.0-11ubuntu2
+ admin
+ 1.4.0-11ubuntu2
+
+
+ amd64
+ 248
+ deb
+ libpam-modules-bin
+ Ubuntu
+ pam
+ 1.4.0-11ubuntu2
+ admin
+ 1.4.0-11ubuntu2
+
+
+ all
+ 312
+ deb
+ libpam-runtime
+ Ubuntu
+ pam
+ 1.4.0-11ubuntu2
+ admin
+ 1.4.0-11ubuntu2
+
+
+ amd64
+ 646
+ deb
+ libpam-systemd
+ Ubuntu
+ systemd
+ 249.11-0ubuntu3.4
+ admin
+ 249.11-0ubuntu3.4
+
+
+ amd64
+ 235
+ deb
+ libpam0g
+ Ubuntu
+ pam
+ 1.4.0-11ubuntu2
+ libs
+ 1.4.0-11ubuntu2
+
+
+ amd64
+ 148
+ deb
+ libparted-fs-resize0
+ Ubuntu
+ parted
+ 3.4-2build1
+ libs
+ 3.4-2build1
+
+
+ amd64
+ 458
+ deb
+ libparted2
+ Ubuntu
+ parted
+ 3.4-2build1
+ libs
+ 3.4-2build1
+
+
+ amd64
+ 357
+ deb
+ libpcap0.8
+ Ubuntu
+ libpcap
+ 1.10.1-4build1
+ libs
+ 1.10.1-4build1
+
+
+ amd64
+ 90
+ deb
+ libpci3
+ Ubuntu
+ pciutils
+ 1:3.7.0-6
+ libs
+ 1:3.7.0-6
+
+
+ amd64
+ 621
+ deb
+ libpcre2-8-0
+ Ubuntu
+ pcre2
+ 10.39-3build1
+ libs
+ 10.39-3build1
+
+
+ amd64
+ 683
+ deb
+ libpcre3
+ Ubuntu
+ pcre3
+ 2:8.39-13ubuntu0.22.04.1
+ libs
+ 2:8.39-13ubuntu0.22.04.1
+
+
+ amd64
+ 28629
+ deb
+ libperl5.34
+ Ubuntu
+ perl
+ 5.34.0-3ubuntu1
+ libs
+ 5.34.0-3ubuntu1
+
+
+ amd64
+ 68
+ deb
+ libpipeline1
+ Ubuntu
+ libpipeline
+ 1.5.5-1
+ libs
+ 1.5.5-1
+
+
+ amd64
+ 419
+ deb
+ libplymouth5
+ Ubuntu
+ plymouth
+ 0.9.5+git20211018-1ubuntu3
+ libs
+ 0.9.5+git20211018-1ubuntu3
+
+
+ amd64
+ 353
+ deb
+ libpng16-16
+ Ubuntu
+ libpng1.6
+ 1.6.37-3build5
+ libs
+ 1.6.37-3build5
+
+
+ amd64
+ 80
+ deb
+ libpolkit-agent-1-0
+ Ubuntu
+ policykit-1
+ 0.105-33
+ libs
+ 0.105-33
+
+
+ amd64
+ 158
+ deb
+ libpolkit-gobject-1-0
+ Ubuntu
+ policykit-1
+ 0.105-33
+ libs
+ 0.105-33
+
+
+ amd64
+ 120
+ deb
+ libpopt0
+ Ubuntu
+ popt
+ 1.18-3build1
+ libs
+ 1.18-3build1
+
+
+ amd64
+ 111
+ deb
+ libproc-processtable-perl
+ Ubuntu
+
+ 0.634-1build1
+ perl
+ 0.634-1build1
+
+
+ amd64
+ 131
+ deb
+ libprocps8
+ Ubuntu
+ procps
+ 2:3.3.17-6ubuntu2
+ libs
+ 2:3.3.17-6ubuntu2
+
+
+ amd64
+ 95
+ deb
+ libpsl5
+ Ubuntu
+ libpsl
+ 0.21.0-1.2build2
+ libs
+ 0.21.0-1.2build2
+
+
+ amd64
+ 39
+ deb
+ libpython3-stdlib
+ Ubuntu
+ python3-defaults
+ 3.10.4-0ubuntu2
+ python
+ 3.10.4-0ubuntu2
+
+
+ amd64
+ 5773
+ deb
+ libpython3.10
+ Ubuntu
+ python3.10
+ 3.10.4-3ubuntu0.1
+ libs
+ 3.10.4-3ubuntu0.1
+
+
+ amd64
+ 5086
+ deb
+ libpython3.10-minimal
+ Ubuntu
+ python3.10
+ 3.10.4-3ubuntu0.1
+ python
+ 3.10.4-3ubuntu0.1
+
+
+ amd64
+ 8031
+ deb
+ libpython3.10-stdlib
+ Ubuntu
+ python3.10
+ 3.10.4-3ubuntu0.1
+ python
+ 3.10.4-3ubuntu0.1
+
+
+ amd64
+ 3481
+ deb
+ libqmi-glib5
+ Ubuntu
+ libqmi
+ 1.30.4-1
+ libs
+ 1.30.4-1
+
+
+ amd64
+ 31
+ deb
+ libqmi-proxy
+ Ubuntu
+ libqmi
+ 1.30.4-1
+ net
+ 1.30.4-1
+
+
+ amd64
+ 461
+ deb
+ libreadline8
+ Ubuntu
+ readline
+ 8.1.2-1
+ libs
+ 8.1.2-1
+
+
+ amd64
+ 141
+ deb
+ librtmp1
+ Ubuntu
+ rtmpdump
+ 2.4+20151223.gitfa8646d.1-2build4
+ libs
+ 2.4+20151223.gitfa8646d.1-2build4
+
+
+ amd64
+ 170
+ deb
+ libsasl2-2
+ Ubuntu
+ cyrus-sasl2
+ 2.1.27+dfsg2-3ubuntu1
+ libs
+ 2.1.27+dfsg2-3ubuntu1
+
+
+ amd64
+ 267
+ deb
+ libsasl2-modules
+ Ubuntu
+ cyrus-sasl2
+ 2.1.27+dfsg2-3ubuntu1
+ libs
+ 2.1.27+dfsg2-3ubuntu1
+
+
+ amd64
+ 97
+ deb
+ libsasl2-modules-db
+ Ubuntu
+ cyrus-sasl2
+ 2.1.27+dfsg2-3ubuntu1
+ libs
+ 2.1.27+dfsg2-3ubuntu1
+
+
+ amd64
+ 145
+ deb
+ libseccomp2
+ Ubuntu
+ libseccomp
+ 2.5.3-2ubuntu2
+ libs
+ 2.5.3-2ubuntu2
+
+
+ amd64
+ 207
+ deb
+ libselinux1
+ Ubuntu
+ libselinux
+ 3.3-1build2
+ libs
+ 3.3-1build2
+
+
+ all
+ 37
+ deb
+ libsemanage-common
+ Ubuntu
+ libsemanage
+ 3.3-1build2
+ libs
+ 3.3-1build2
+
+
+ amd64
+ 300
+ deb
+ libsemanage2
+ Ubuntu
+ libsemanage
+ 3.3-1build2
+ libs
+ 3.3-1build2
+
+
+ amd64
+ 735
+ deb
+ libsepol2
+ Ubuntu
+ libsepol
+ 3.3-1build1
+ libs
+ 3.3-1build1
+
+
+ amd64
+ 294
+ deb
+ libsgutils2-2
+ Ubuntu
+ sg3-utils
+ 1.46-1build1
+ libs
+ 1.46-1build1
+
+
+ amd64
+ 49
+ deb
+ libsigsegv2
+ Ubuntu
+ libsigsegv
+ 2.13-1ubuntu3
+ libs
+ 2.13-1ubuntu3
+
+
+ amd64
+ 1628
+ deb
+ libslang2
+ Ubuntu
+ slang2
+ 2.3.2-5build4
+ libs
+ 2.3.2-5build4
+
+
+ amd64
+ 209
+ deb
+ libsmartcols1
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ libs
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 304
+ deb
+ libsmbios-c2
+ Ubuntu
+ libsmbios
+ 2.4.3-1build1
+ libs
+ 2.4.3-1build1
+
+
+ amd64
+ 402
+ deb
+ libsodium23
+ Ubuntu
+ libsodium
+ 1.0.18-1build2
+ libs
+ 1.0.18-1build2
+
+
+ all
+ 40
+ deb
+ libsort-naturally-perl
+ Ubuntu
+
+ 1.03-2
+ perl
+ 1.03-2
+
+
+ amd64
+ 1602
+ deb
+ libsqlite3-0
+ Ubuntu
+ sqlite3
+ 3.37.2-2
+ libs
+ 3.37.2-2
+
+
+ amd64
+ 113
+ deb
+ libss2
+ Ubuntu
+ e2fsprogs
+ 1.46.5-2ubuntu1.1
+ libs
+ 1.46.5-2ubuntu1.1
+
+
+ amd64
+ 486
+ deb
+ libssh-4
+ Ubuntu
+ libssh
+ 0.9.6-2build1
+ libs
+ 0.9.6-2build1
+
+
+ amd64
+ 5822
+ deb
+ libssl3
+ Ubuntu
+ openssl
+ 3.0.2-0ubuntu1.6
+ libs
+ 3.0.2-0ubuntu1.6
+
+
+ amd64
+ 2750
+ deb
+ libstdc++6
+ Ubuntu
+ gcc-12
+ 12-20220319-1ubuntu1
+ libs
+ 12-20220319-1ubuntu1
+
+
+ amd64
+ 839
+ deb
+ libstemmer0d
+ Ubuntu
+ snowball
+ 2.2.0-1build1
+ libs
+ 2.2.0-1build1
+
+
+ amd64
+ 993
+ deb
+ libsystemd0
+ Ubuntu
+ systemd
+ 249.11-0ubuntu3.4
+ libs
+ 249.11-0ubuntu3.4
+
+
+ amd64
+ 133
+ deb
+ libtasn1-6
+ Ubuntu
+
+ 4.18.0-4build1
+ libs
+ 4.18.0-4build1
+
+
+ amd64
+ 4091
+ deb
+ libtcl8.6
+ Ubuntu
+ tcl8.6
+ 8.6.12+dfsg-1build1
+ libs
+ 8.6.12+dfsg-1build1
+
+
+ amd64
+ 72
+ deb
+ libterm-readkey-perl
+ Ubuntu
+
+ 2.38-1build4
+ perl
+ 2.38-1build4
+
+
+ amd64
+ 45
+ deb
+ libtext-charwidth-perl
+ Ubuntu
+
+ 0.04-10build3
+ perl
+ 0.04-10build3
+
+
+ amd64
+ 52
+ deb
+ libtext-iconv-perl
+ Ubuntu
+
+ 1.7-7build3
+ perl
+ 1.7-7build3
+
+
+ all
+ 25
+ deb
+ libtext-wrapi18n-perl
+ Ubuntu
+
+ 0.06-9
+ perl
+ 0.06-9
+
+
+ amd64
+ 558
+ deb
+ libtinfo6
+ Ubuntu
+ ncurses
+ 6.3-2
+ libs
+ 6.3-2
+
+
+ all
+ 32
+ deb
+ libtirpc-common
+ Ubuntu
+ libtirpc
+ 1.3.2-2build1
+ libs
+ 1.3.2-2build1
+
+
+ amd64
+ 215
+ deb
+ libtirpc3
+ Ubuntu
+ libtirpc
+ 1.3.2-2build1
+ libs
+ 1.3.2-2build1
+
+
+ amd64
+ 623
+ deb
+ libtss2-esys-3.0.2-0
+ Ubuntu
+ tpm2-tss
+ 3.2.0-1ubuntu1
+ libs
+ 3.2.0-1ubuntu1
+
+
+ amd64
+ 344
+ deb
+ libtss2-mu0
+ Ubuntu
+ tpm2-tss
+ 3.2.0-1ubuntu1
+ libs
+ 3.2.0-1ubuntu1
+
+
+ amd64
+ 170
+ deb
+ libtss2-sys1
+ Ubuntu
+ tpm2-tss
+ 3.2.0-1ubuntu1
+ libs
+ 3.2.0-1ubuntu1
+
+
+ amd64
+ 57
+ deb
+ libtss2-tcti-cmd0
+ Ubuntu
+ tpm2-tss
+ 3.2.0-1ubuntu1
+ libs
+ 3.2.0-1ubuntu1
+
+
+ amd64
+ 52
+ deb
+ libtss2-tcti-device0
+ Ubuntu
+ tpm2-tss
+ 3.2.0-1ubuntu1
+ libs
+ 3.2.0-1ubuntu1
+
+
+ amd64
+ 56
+ deb
+ libtss2-tcti-mssim0
+ Ubuntu
+ tpm2-tss
+ 3.2.0-1ubuntu1
+ libs
+ 3.2.0-1ubuntu1
+
+
+ amd64
+ 56
+ deb
+ libtss2-tcti-swtpm0
+ Ubuntu
+ tpm2-tss
+ 3.2.0-1ubuntu1
+ libs
+ 3.2.0-1ubuntu1
+
+
+ amd64
+ 208
+ deb
+ libuchardet0
+ Ubuntu
+ uchardet
+ 0.0.7-1build2
+ libs
+ 0.0.7-1build2
+
+
+ amd64
+ 345
+ deb
+ libudev1
+ Ubuntu
+ systemd
+ 249.11-0ubuntu3.4
+ libs
+ 249.11-0ubuntu3.4
+
+
+ amd64
+ 835
+ deb
+ libudisks2-0
+ Ubuntu
+ udisks2
+ 2.9.4-1ubuntu2
+ libs
+ 2.9.4-1ubuntu2
+
+
+ amd64
+ 1746
+ deb
+ libunistring2
+ Ubuntu
+ libunistring
+ 1.0-1
+ libs
+ 1.0-1
+
+
+ amd64
+ 196
+ deb
+ libunwind8
+ Ubuntu
+ libunwind
+ 1.3.2-2build2
+ libs
+ 1.3.2-2build2
+
+
+ amd64
+ 331
+ deb
+ liburcu8
+ Ubuntu
+ liburcu
+ 0.13.1-1
+ libs
+ 0.13.1-1
+
+
+ amd64
+ 144
+ deb
+ libusb-1.0-0
+ Ubuntu
+ libusb-1.0
+ 2:1.0.25-1ubuntu2
+ libs
+ 2:1.0.25-1ubuntu2
+
+
+ amd64
+ 51
+ deb
+ libutempter0
+ Ubuntu
+ libutempter
+ 1.2.1-2build2
+ libs
+ 1.2.1-2build2
+
+
+ amd64
+ 134
+ deb
+ libuuid1
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ libs
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 252
+ deb
+ libuv1
+ Ubuntu
+
+ 1.43.0-1
+ libs
+ 1.43.0-1
+
+
+ amd64
+ 180
+ deb
+ libvolume-key1
+ Ubuntu
+ volume-key
+ 0.3.12-3.1build3
+ libs
+ 0.3.12-3.1build3
+
+
+ amd64
+ 109
+ deb
+ libwrap0
+ Ubuntu
+ tcp-wrappers
+ 7.6.q-31build2
+ libs
+ 7.6.q-31build2
+
+
+ amd64
+ 1386
+ deb
+ libx11-6
+ Ubuntu
+ libx11
+ 2:1.7.5-1
+ libs
+ 2:1.7.5-1
+
+
+ all
+ 1429
+ deb
+ libx11-data
+ Ubuntu
+ libx11
+ 2:1.7.5-1
+ x11
+ 2:1.7.5-1
+
+
+ amd64
+ 35
+ deb
+ libxau6
+ Ubuntu
+ libxau
+ 1:1.0.9-1build5
+ libs
+ 1:1.0.9-1build5
+
+
+ amd64
+ 206
+ deb
+ libxcb1
+ Ubuntu
+ libxcb
+ 1.14-3ubuntu3
+ libs
+ 1.14-3ubuntu3
+
+
+ amd64
+ 43
+ deb
+ libxdmcp6
+ Ubuntu
+ libxdmcp
+ 1:1.1.3-0ubuntu5
+ libs
+ 1:1.1.3-0ubuntu5
+
+
+ amd64
+ 110
+ deb
+ libxext6
+ Ubuntu
+ libxext
+ 2:1.3.4-1build1
+ libs
+ 2:1.3.4-1build1
+
+
+ all
+ 60
+ deb
+ libxml-treepp-perl
+ Ubuntu
+
+ 0.43-1
+ perl
+ 0.43-1
+
+
+ amd64
+ 2096
+ deb
+ libxml2
+ Ubuntu
+
+ 2.9.13+dfsg-1ubuntu0.1
+ libs
+ 2.9.13+dfsg-1ubuntu0.1
+
+
+ amd64
+ 198
+ deb
+ libxmlb2
+ Ubuntu
+ libxmlb
+ 0.3.6-2build1
+ libs
+ 0.3.6-2build1
+
+
+ amd64
+ 435
+ deb
+ libxmlsec1
+ Ubuntu
+ xmlsec1
+ 1.2.33-1build2
+ libs
+ 1.2.33-1build2
+
+
+ amd64
+ 304
+ deb
+ libxmlsec1-openssl
+ Ubuntu
+ xmlsec1
+ 1.2.33-1build2
+ libs
+ 1.2.33-1build2
+
+
+ amd64
+ 41
+ deb
+ libxmuu1
+ Ubuntu
+ libxmu
+ 2:1.1.3-3
+ libs
+ 2:1.1.3-3
+
+
+ amd64
+ 503
+ deb
+ libxslt1.1
+ Ubuntu
+ libxslt
+ 1.1.34-4build2
+ libs
+ 1.1.34-4build2
+
+
+ amd64
+ 114
+ deb
+ libxtables12
+ Ubuntu
+ iptables
+ 1.8.7-1ubuntu5
+ libs
+ 1.8.7-1ubuntu5
+
+
+ amd64
+ 97
+ deb
+ libxxhash0
+ Ubuntu
+ xxhash
+ 0.8.1-1
+ libs
+ 0.8.1-1
+
+
+ amd64
+ 144
+ deb
+ libyaml-0-2
+ Ubuntu
+ libyaml
+ 0.2.2-1build2
+ libs
+ 0.2.2-1build2
+
+
+ amd64
+ 846
+ deb
+ libzstd1
+ Ubuntu
+ libzstd
+ 1.4.8+dfsg-3build1
+ libs
+ 1.4.8+dfsg-3build1
+
+
+ all
+ 63
+ deb
+ linux-base
+ Ubuntu
+
+ 4.5ubuntu9
+ kernel
+ 4.5ubuntu9
+
+
+ all
+ 75459
+ deb
+ linux-headers-5.15.0-41
+ Ubuntu
+ linux
+ 5.15.0-41.44
+ devel
+ 5.15.0-41.44
+
+
+ amd64
+ 23758
+ deb
+ linux-headers-5.15.0-41-generic
+ Ubuntu
+ linux
+ 5.15.0-41.44
+ devel
+ 5.15.0-41.44
+
+
+ amd64
+ 20
+ deb
+ linux-headers-generic
+ Ubuntu
+ linux-meta
+ 5.15.0.41.43
+ kernel
+ 5.15.0.41.43
+
+
+ amd64
+ 20
+ deb
+ linux-headers-virtual
+ Ubuntu
+ linux-meta
+ 5.15.0.41.43
+ kernel
+ 5.15.0.41.43
+
+
+ amd64
+ 10860
+ deb
+ linux-image-5.15.0-41-generic
+ Ubuntu
+ linux-signed
+ 5.15.0-41.44
+ kernel
+ 5.15.0-41.44
+
+
+ amd64
+ 20
+ deb
+ linux-image-virtual
+ Ubuntu
+ linux-meta
+ 5.15.0.41.43
+ kernel
+ 5.15.0.41.43
+
+
+ amd64
+ 108804
+ deb
+ linux-modules-5.15.0-41-generic
+ Ubuntu
+ linux
+ 5.15.0-41.44
+ kernel
+ 5.15.0-41.44
+
+
+ amd64
+ 20
+ deb
+ linux-virtual
+ Ubuntu
+ linux-meta
+ 5.15.0.41.43
+ kernel
+ 5.15.0.41.43
+
+
+ all
+ 17064
+ deb
+ locales
+ Ubuntu
+ glibc
+ 2.35-0ubuntu3
+ localization
+ 2.35-0ubuntu3
+
+
+ amd64
+ 888
+ deb
+ login
+ Ubuntu
+ shadow
+ 1:4.8.1-2ubuntu2
+ admin
+ 1:4.8.1-2ubuntu2
+
+
+ amd64
+ 167
+ deb
+ logrotate
+ Ubuntu
+
+ 3.19.0-1ubuntu1.1
+ admin
+ 3.19.0-1ubuntu1.1
+
+
+ amd64
+ 97
+ deb
+ logsave
+ Ubuntu
+ e2fsprogs
+ 1.46.5-2ubuntu1.1
+ admin
+ 1.46.5-2ubuntu1.1
+
+
+ all
+ 58
+ deb
+ lsb-base
+ Ubuntu
+ lsb
+ 11.1.0ubuntu4
+ misc
+ 11.1.0ubuntu4
+
+
+ all
+ 66
+ deb
+ lsb-release
+ Ubuntu
+ lsb
+ 11.1.0ubuntu4
+ misc
+ 11.1.0ubuntu4
+
+
+ amd64
+ 921
+ deb
+ lshw
+ Ubuntu
+
+ 02.19.git.2021.06.19.996aaad9c7-2build1
+ utils
+ 02.19.git.2021.06.19.996aaad9c7-2build1
+
+
+ amd64
+ 447
+ deb
+ lsof
+ Ubuntu
+
+ 4.93.2+dfsg-1.1build2
+ utils
+ 4.93.2+dfsg-1.1build2
+
+
+ amd64
+ 4032
+ deb
+ lvm2
+ Ubuntu
+
+ 2.03.11-2.1ubuntu4
+ admin
+ 2.03.11-2.1ubuntu4
+
+
+ all
+ 24
+ deb
+ lxd-agent-loader
+ Ubuntu
+
+ 0.5
+ misc
+ 0.5
+
+
+ amd64
+ 2824
+ deb
+ man-db
+ Ubuntu
+
+ 2.10.2-1
+ doc
+ 2.10.2-1
+
+
+ all
+ 1669
+ deb
+ manpages
+ Ubuntu
+
+ 5.10-1ubuntu1
+ doc
+ 5.10-1ubuntu1
+
+
+ amd64
+ 229
+ deb
+ mawk
+ Ubuntu
+
+ 1.3.4.20200120-3
+ utils
+ 1.3.4.20200120-3
+
+
+ amd64
+ 1180
+ deb
+ mdadm
+ Ubuntu
+
+ 4.2-0ubuntu1
+ admin
+ 4.2-0ubuntu1
+
+
+ all
+ 97
+ deb
+ media-types
+ Ubuntu
+
+ 7.0.0
+ net
+ 7.0.0
+
+
+ amd64
+ 4340
+ deb
+ modemmanager
+ Ubuntu
+
+ 1.18.6-1
+ net
+ 1.18.6-1
+
+
+ all
+ 47
+ deb
+ motd-news-config
+ Ubuntu
+ base-files
+ 12ubuntu4.1
+ admin
+ 12ubuntu4.1
+
+
+ amd64
+ 389
+ deb
+ mount
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ admin
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 157
+ deb
+ mtr-tiny
+ Ubuntu
+ mtr
+ 0.95-1
+ net
+ 0.95-1
+
+
+ amd64
+ 1227
+ deb
+ multipath-tools
+ Ubuntu
+
+ 0.8.8-1ubuntu1
+ admin
+ 0.8.8-1ubuntu1
+
+
+ amd64
+ 860
+ deb
+ nano
+ Ubuntu
+
+ 6.2-1
+ editors
+ 6.2-1
+
+
+ all
+ 393
+ deb
+ ncurses-base
+ Ubuntu
+ ncurses
+ 6.3-2
+ utils
+ 6.3-2
+
+
+ amd64
+ 646
+ deb
+ ncurses-bin
+ Ubuntu
+ ncurses
+ 6.3-2
+ utils
+ 6.3-2
+
+
+ all
+ 4249
+ deb
+ ncurses-term
+ Ubuntu
+ ncurses
+ 6.3-2
+ misc
+ 6.3-2
+
+
+ all
+ 500
+ deb
+ needrestart
+ Ubuntu
+
+ 3.5-5ubuntu2.1
+ admin
+ 3.5-5ubuntu2.1
+
+
+ amd64
+ 800
+ deb
+ net-tools
+ Ubuntu
+
+ 1.60+git20181103.0eebece-1ubuntu5
+ net
+ 1.60+git20181103.0eebece-1ubuntu5
+
+
+ all
+ 41
+ deb
+ netbase
+ Ubuntu
+
+ 6.3
+ admin
+ 6.3
+
+
+ amd64
+ 106
+ deb
+ netcat-openbsd
+ Ubuntu
+
+ 1.218-4ubuntu1
+ net
+ 1.218-4ubuntu1
+
+
+ amd64
+ 377
+ deb
+ netplan.io
+ Ubuntu
+
+ 0.104-0ubuntu2
+ net
+ 0.104-0ubuntu2
+
+
+ all
+ 69
+ deb
+ networkd-dispatcher
+ Ubuntu
+
+ 2.1-2ubuntu0.22.04.2
+ utils
+ 2.1-2ubuntu0.22.04.2
+
+
+ amd64
+ 177
+ deb
+ nftables
+ Ubuntu
+
+ 1.0.2-1ubuntu2
+ net
+ 1.0.2-1ubuntu2
+
+
+ amd64
+ 1300
+ deb
+ ntfs-3g
+ Ubuntu
+
+ 1:2021.8.22-3ubuntu1.1
+ otherosfs
+ 1:2021.8.22-3ubuntu1.1
+
+
+ amd64
+ 1221
+ deb
+ open-iscsi
+ Ubuntu
+
+ 2.1.5-1ubuntu1
+ net
+ 2.1.5-1ubuntu1
+
+
+ amd64
+ 3014
+ deb
+ open-vm-tools
+ Ubuntu
+
+ 2:11.3.5-1ubuntu4
+ admin
+ 2:11.3.5-1ubuntu4
+
+
+ amd64
+ 3079
+ deb
+ openssh-client
+ Ubuntu
+ openssh
+ 1:8.9p1-3
+ net
+ 1:8.9p1-3
+
+
+ amd64
+ 1501
+ deb
+ openssh-server
+ Ubuntu
+ openssh
+ 1:8.9p1-3
+ net
+ 1:8.9p1-3
+
+
+ amd64
+ 101
+ deb
+ openssh-sftp-server
+ Ubuntu
+ openssh
+ 1:8.9p1-3
+ net
+ 1:8.9p1-3
+
+
+ amd64
+ 2053
+ deb
+ openssl
+ Ubuntu
+
+ 3.0.2-0ubuntu1.6
+ utils
+ 3.0.2-0ubuntu1.6
+
+
+ amd64
+ 112
+ deb
+ os-prober
+ Ubuntu
+
+ 1.79ubuntu2
+ utils
+ 1.79ubuntu2
+
+
+ all
+ 66
+ deb
+ overlayroot
+ Ubuntu
+ cloud-initramfs-tools
+ 0.47ubuntu1
+ admin
+ 0.47ubuntu1
+
+
+ amd64
+ 1592
+ deb
+ packagekit
+ Ubuntu
+
+ 1.2.5-2ubuntu2
+ admin
+ 1.2.5-2ubuntu2
+
+
+ amd64
+ 123
+ deb
+ packagekit-tools
+ Ubuntu
+ packagekit
+ 1.2.5-2ubuntu2
+ admin
+ 1.2.5-2ubuntu2
+
+
+ amd64
+ 167
+ deb
+ parted
+ Ubuntu
+
+ 3.4-2build1
+ admin
+ 3.4-2build1
+
+
+ amd64
+ 2321
+ deb
+ passwd
+ Ubuntu
+ shadow
+ 1:4.8.1-2ubuntu2
+ admin
+ 1:4.8.1-2ubuntu2
+
+
+ all
+ 152
+ deb
+ pastebinit
+ Ubuntu
+
+ 1.5.1-1ubuntu1
+ misc
+ 1.5.1-1ubuntu1
+
+
+ amd64
+ 229
+ deb
+ patch
+ Ubuntu
+
+ 2.7.6-7build2
+ vcs
+ 2.7.6-7build2
+
+
+ all
+ 1282
+ deb
+ pci.ids
+ Ubuntu
+
+ 0.0~2022.01.22-1
+ admin
+ 0.0~2022.01.22-1
+
+
+ amd64
+ 172
+ deb
+ pciutils
+ Ubuntu
+
+ 1:3.7.0-6
+ admin
+ 1:3.7.0-6
+
+
+ amd64
+ 717
+ deb
+ perl
+ Ubuntu
+
+ 5.34.0-3ubuntu1
+ perl
+ 5.34.0-3ubuntu1
+
+
+ amd64
+ 7775
+ deb
+ perl-base
+ Ubuntu
+ perl
+ 5.34.0-3ubuntu1
+ perl
+ 5.34.0-3ubuntu1
+
+
+ all
+ 17668
+ deb
+ perl-modules-5.34
+ Ubuntu
+ perl
+ 5.34.0-3ubuntu1
+ libs
+ 5.34.0-3ubuntu1
+
+
+ amd64
+ 92
+ deb
+ pinentry-curses
+ Ubuntu
+ pinentry
+ 1.1.1-1build2
+ utils
+ 1.1.1-1build2
+
+
+ amd64
+ 65
+ deb
+ pkexec
+ Ubuntu
+ policykit-1
+ 0.105-33
+ admin
+ 0.105-33
+
+
+ amd64
+ 924
+ deb
+ plymouth
+ Ubuntu
+
+ 0.9.5+git20211018-1ubuntu3
+ misc
+ 0.9.5+git20211018-1ubuntu3
+
+
+ amd64
+ 82
+ deb
+ plymouth-theme-ubuntu-text
+ Ubuntu
+ plymouth
+ 0.9.5+git20211018-1ubuntu3
+ misc
+ 0.9.5+git20211018-1ubuntu3
+
+
+ amd64
+ 29
+ deb
+ policykit-1
+ Ubuntu
+
+ 0.105-33
+ oldlibs
+ 0.105-33
+
+
+ amd64
+ 520
+ deb
+ polkitd
+ Ubuntu
+ policykit-1
+ 0.105-33
+ admin
+ 0.105-33
+
+
+ all
+ 92
+ deb
+ pollinate
+ Ubuntu
+
+ 4.33-3ubuntu2
+ admin
+ 4.33-3ubuntu2
+
+
+ all
+ 25
+ deb
+ powermgmt-base
+ Ubuntu
+
+ 1.36
+ utils
+ 1.36
+
+
+ amd64
+ 1388
+ deb
+ procps
+ Ubuntu
+
+ 2:3.3.17-6ubuntu2
+ admin
+ 2:3.3.17-6ubuntu2
+
+
+ amd64
+ 452
+ deb
+ psmisc
+ Ubuntu
+
+ 23.4-2build3
+ admin
+ 23.4-2build3
+
+
+ all
+ 330
+ deb
+ publicsuffix
+ Ubuntu
+
+ 20211207.1025-1
+ net
+ 20211207.1025-1
+
+
+ amd64
+ 52
+ deb
+ pwgen
+ Ubuntu
+
+ 2.08-2build1
+ admin
+ 2.08-2build1
+
+
+ all
+ 188
+ deb
+ python-apt-common
+ Ubuntu
+ python-apt
+ 2.3.0ubuntu2
+ python
+ 2.3.0ubuntu2
+
+
+ all
+ 26407
+ deb
+ python-babel-localedata
+ Ubuntu
+ python-babel
+ 2.8.0+dfsg.1-7
+ python
+ 2.8.0+dfsg.1-7
+
+
+ amd64
+ 90
+ deb
+ python3
+ Ubuntu
+ python3-defaults
+ 3.10.4-0ubuntu2
+ python
+ 3.10.4-0ubuntu2
+
+
+ all
+ 594
+ deb
+ python3-apport
+ Ubuntu
+ apport
+ 2.20.11-0ubuntu82.1
+ python
+ 2.20.11-0ubuntu82.1
+
+
+ amd64
+ 705
+ deb
+ python3-apt
+ Ubuntu
+ python-apt
+ 2.3.0ubuntu2
+ python
+ 2.3.0ubuntu2
+
+
+ all
+ 207
+ deb
+ python3-attr
+ Ubuntu
+ python-attrs
+ 21.2.0-1
+ python
+ 21.2.0-1
+
+
+ all
+ 141
+ deb
+ python3-automat
+ Ubuntu
+ automat
+ 20.2.0-1
+ python
+ 20.2.0-1
+
+
+ all
+ 418
+ deb
+ python3-babel
+ Ubuntu
+ python-babel
+ 2.8.0+dfsg.1-7
+ python
+ 2.8.0+dfsg.1-7
+
+
+ amd64
+ 90
+ deb
+ python3-bcrypt
+ Ubuntu
+ python-bcrypt
+ 3.2.0-1build1
+ python
+ 3.2.0-1build1
+
+
+ all
+ 55
+ deb
+ python3-blinker
+ Ubuntu
+ blinker
+ 1.4+dfsg1-0.4
+ python
+ 1.4+dfsg1-0.4
+
+
+ all
+ 324
+ deb
+ python3-certifi
+ Ubuntu
+ python-certifi
+ 2020.6.20-1
+ python
+ 2020.6.20-1
+
+
+ amd64
+ 218
+ deb
+ python3-cffi-backend
+ Ubuntu
+ python-cffi
+ 1.15.0-1build2
+ python
+ 1.15.0-1build2
+
+
+ all
+ 1068
+ deb
+ python3-chardet
+ Ubuntu
+ chardet
+ 4.0.0-1
+ python
+ 4.0.0-1
+
+
+ all
+ 366
+ deb
+ python3-click
+ Ubuntu
+ python-click
+ 8.0.3-1
+ python
+ 8.0.3-1
+
+
+ all
+ 91
+ deb
+ python3-colorama
+ Ubuntu
+ python-colorama
+ 0.4.4-1
+ python
+ 0.4.4-1
+
+
+ all
+ 59
+ deb
+ python3-commandnotfound
+ Ubuntu
+ command-not-found
+ 22.04.0
+ python
+ 22.04.0
+
+
+ all
+ 160
+ deb
+ python3-configobj
+ Ubuntu
+ configobj
+ 5.0.6-5
+ python
+ 5.0.6-5
+
+
+ all
+ 40
+ deb
+ python3-constantly
+ Ubuntu
+ constantly
+ 15.1.0-2
+ python
+ 15.1.0-2
+
+
+ amd64
+ 1587
+ deb
+ python3-cryptography
+ Ubuntu
+ python-cryptography
+ 3.4.8-1ubuntu2
+ python
+ 3.4.8-1ubuntu2
+
+
+ amd64
+ 417
+ deb
+ python3-dbus
+ Ubuntu
+ dbus-python
+ 1.2.18-3build1
+ python
+ 1.2.18-3build1
+
+
+ all
+ 18
+ deb
+ python3-debconf
+ Ubuntu
+ debconf
+ 1.5.79ubuntu1
+ python
+ 1.5.79ubuntu1
+
+
+ all
+ 552
+ deb
+ python3-debian
+ Ubuntu
+ python-debian
+ 0.1.43ubuntu1
+ python
+ 0.1.43ubuntu1
+
+
+ all
+ 77
+ deb
+ python3-distro
+ Ubuntu
+ python-distro
+ 1.7.0-1
+ python
+ 1.7.0-1
+
+
+ all
+ 35
+ deb
+ python3-distro-info
+ Ubuntu
+ distro-info
+ 1.1build1
+ python
+ 1.1build1
+
+
+ all
+ 632
+ deb
+ python3-distupgrade
+ Ubuntu
+ ubuntu-release-upgrader
+ 1:22.04.11
+ python
+ 1:22.04.11
+
+
+ all
+ 675
+ deb
+ python3-distutils
+ Ubuntu
+ python3-stdlib-extensions
+ 3.10.4-0ubuntu1
+ python
+ 3.10.4-0ubuntu1
+
+
+ amd64
+ 57
+ deb
+ python3-gdbm
+ Ubuntu
+ python3-stdlib-extensions
+ 3.10.4-0ubuntu1
+ python
+ 3.10.4-0ubuntu1
+
+
+ amd64
+ 746
+ deb
+ python3-gi
+ Ubuntu
+ pygobject
+ 3.42.0-3build1
+ python
+ 3.42.0-3build1
+
+
+ all
+ 166
+ deb
+ python3-hamcrest
+ Ubuntu
+ pyhamcrest
+ 2.0.2-2
+ python
+ 2.0.2-2
+
+
+ all
+ 131
+ deb
+ python3-httplib2
+ Ubuntu
+ python-httplib2
+ 0.20.2-2
+ python
+ 0.20.2-2
+
+
+ all
+ 228
+ deb
+ python3-hyperlink
+ Ubuntu
+ hyperlink
+ 21.0.0-3
+ python
+ 21.0.0-3
+
+
+ all
+ 299
+ deb
+ python3-idna
+ Ubuntu
+ python-idna
+ 3.3-1
+ python
+ 3.3-1
+
+
+ all
+ 67
+ deb
+ python3-importlib-metadata
+ Ubuntu
+ python-importlib-metadata
+ 4.6.4-1
+ python
+ 4.6.4-1
+
+
+ all
+ 99
+ deb
+ python3-incremental
+ Ubuntu
+ incremental
+ 21.3.0-1
+ python
+ 21.3.0-1
+
+
+ all
+ 186
+ deb
+ python3-jeepney
+ Ubuntu
+ jeepney
+ 0.7.1-3
+ python
+ 0.7.1-3
+
+
+ all
+ 544
+ deb
+ python3-jinja2
+ Ubuntu
+ jinja2
+ 3.0.3-1
+ python
+ 3.0.3-1
+
+
+ all
+ 44
+ deb
+ python3-json-pointer
+ Ubuntu
+ python-json-pointer
+ 2.0-0ubuntu1
+ python
+ 2.0-0ubuntu1
+
+
+ all
+ 59
+ deb
+ python3-jsonpatch
+ Ubuntu
+ python-json-patch
+ 1.32-2
+ python
+ 1.32-2
+
+
+ all
+ 259
+ deb
+ python3-jsonschema
+ Ubuntu
+ python-jsonschema
+ 3.2.0-0ubuntu2
+ python
+ 3.2.0-0ubuntu2
+
+
+ all
+ 81
+ deb
+ python3-jwt
+ Ubuntu
+ pyjwt
+ 2.3.0-1
+ python
+ 2.3.0-1
+
+
+ all
+ 154
+ deb
+ python3-keyring
+ Ubuntu
+ python-keyring
+ 23.5.0-1
+ python
+ 23.5.0-1
+
+
+ all
+ 1762
+ deb
+ python3-launchpadlib
+ Ubuntu
+ python-launchpadlib
+ 1.10.16-1
+ python
+ 1.10.16-1
+
+
+ all
+ 183
+ deb
+ python3-lazr.restfulclient
+ Ubuntu
+ lazr.restfulclient
+ 0.14.4-1
+ python
+ 0.14.4-1
+
+
+ all
+ 75
+ deb
+ python3-lazr.uri
+ Ubuntu
+ lazr.uri
+ 1.0.6-2
+ python
+ 1.0.6-2
+
+
+ all
+ 367
+ deb
+ python3-lib2to3
+ Ubuntu
+ python3-stdlib-extensions
+ 3.10.4-0ubuntu1
+ python
+ 3.10.4-0ubuntu1
+
+
+ amd64
+ 50
+ deb
+ python3-markupsafe
+ Ubuntu
+ markupsafe
+ 2.0.1-2build1
+ python
+ 2.0.1-2build1
+
+
+ amd64
+ 122
+ deb
+ python3-minimal
+ Ubuntu
+ python3-defaults
+ 3.10.4-0ubuntu2
+ python
+ 3.10.4-0ubuntu2
+
+
+ all
+ 226
+ deb
+ python3-more-itertools
+ Ubuntu
+ more-itertools
+ 8.10.0-2
+ python
+ 8.10.0-2
+
+
+ amd64
+ 54
+ deb
+ python3-netifaces
+ Ubuntu
+ netifaces
+ 0.11.0-1build2
+ python
+ 0.11.0-1build2
+
+
+ amd64
+ 111
+ deb
+ python3-newt
+ Ubuntu
+ newt
+ 0.52.21-5ubuntu2
+ python
+ 0.52.21-5ubuntu2
+
+
+ all
+ 556
+ deb
+ python3-oauthlib
+ Ubuntu
+ python-oauthlib
+ 3.2.0-1
+ python
+ 3.2.0-1
+
+
+ all
+ 238
+ deb
+ python3-openssl
+ Ubuntu
+ pyopenssl
+ 21.0.0-1
+ python
+ 21.0.0-1
+
+
+ all
+ 200
+ deb
+ python3-pexpect
+ Ubuntu
+ pexpect
+ 4.8.0-2ubuntu1
+ python
+ 4.8.0-2ubuntu1
+
+
+ all
+ 580
+ deb
+ python3-pkg-resources
+ Ubuntu
+ setuptools
+ 59.6.0-1.2
+ python
+ 59.6.0-1.2
+
+
+ all
+ 180
+ deb
+ python3-problem-report
+ Ubuntu
+ apport
+ 2.20.11-0ubuntu82.1
+ python
+ 2.20.11-0ubuntu82.1
+
+
+ all
+ 59
+ deb
+ python3-ptyprocess
+ Ubuntu
+ ptyprocess
+ 0.7.0-3
+ python
+ 0.7.0-3
+
+
+ all
+ 390
+ deb
+ python3-pyasn1
+ Ubuntu
+ pyasn1
+ 0.4.8-1
+ python
+ 0.4.8-1
+
+
+ all
+ 363
+ deb
+ python3-pyasn1-modules
+ Ubuntu
+ python-pyasn1-modules
+ 0.2.1-1
+ python
+ 0.2.1-1
+
+
+ all
+ 298
+ deb
+ python3-pyparsing
+ Ubuntu
+ pyparsing
+ 2.4.7-1
+ python
+ 2.4.7-1
+
+
+ amd64
+ 249
+ deb
+ python3-pyrsistent
+ Ubuntu
+ pyrsistent
+ 0.18.1-1build1
+ python
+ 0.18.1-1build1
+
+
+ all
+ 230
+ deb
+ python3-requests
+ Ubuntu
+ requests
+ 2.25.1+dfsg-2
+ python
+ 2.25.1+dfsg-2
+
+
+ all
+ 56
+ deb
+ python3-secretstorage
+ Ubuntu
+ python-secretstorage
+ 3.3.1-1
+ python
+ 3.3.1-1
+
+
+ all
+ 459
+ deb
+ python3-serial
+ Ubuntu
+ pyserial
+ 3.5-1
+ python
+ 3.5-1
+
+
+ all
+ 53
+ deb
+ python3-service-identity
+ Ubuntu
+ python-service-identity
+ 18.1.0-6
+ python
+ 18.1.0-6
+
+
+ all
+ 1747
+ deb
+ python3-setuptools
+ Ubuntu
+ setuptools
+ 59.6.0-1.2
+ python
+ 59.6.0-1.2
+
+
+ all
+ 59
+ deb
+ python3-six
+ Ubuntu
+ six
+ 1.16.0-3ubuntu1
+ python
+ 1.16.0-3ubuntu1
+
+
+ all
+ 174
+ deb
+ python3-software-properties
+ Ubuntu
+ software-properties
+ 0.99.22.2
+ python
+ 0.99.22.2
+
+
+ amd64
+ 193
+ deb
+ python3-systemd
+ Ubuntu
+ python-systemd
+ 234-3ubuntu2
+ python
+ 234-3ubuntu2
+
+
+ all
+ 12654
+ deb
+ python3-twisted
+ Ubuntu
+ twisted
+ 22.1.0-2ubuntu2.1
+ python
+ 22.1.0-2ubuntu2.1
+
+
+ all
+ 121
+ deb
+ python3-tz
+ Ubuntu
+ python-tz
+ 2022.1-1
+ python
+ 2022.1-1
+
+
+ all
+ 255
+ deb
+ python3-update-manager
+ Ubuntu
+ update-manager
+ 1:22.04.9
+ python
+ 1:22.04.9
+
+
+ all
+ 457
+ deb
+ python3-urllib3
+ Ubuntu
+ python-urllib3
+ 1.26.5-1~exp1
+ python
+ 1.26.5-1~exp1
+
+
+ all
+ 365
+ deb
+ python3-wadllib
+ Ubuntu
+ python-wadllib
+ 1.3.6-1
+ python
+ 1.3.6-1
+
+
+ amd64
+ 529
+ deb
+ python3-yaml
+ Ubuntu
+ pyyaml
+ 5.4.1-1ubuntu1
+ python
+ 5.4.1-1ubuntu1
+
+
+ all
+ 25
+ deb
+ python3-zipp
+ Ubuntu
+ python-zipp
+ 1.0.0-3
+ python
+ 1.0.0-3
+
+
+ amd64
+ 961
+ deb
+ python3-zope.interface
+ Ubuntu
+ zope.interface
+ 5.4.0-1build1
+ zope
+ 5.4.0-1build1
+
+
+ amd64
+ 611
+ deb
+ python3.10
+ Ubuntu
+
+ 3.10.4-3ubuntu0.1
+ python
+ 3.10.4-3ubuntu0.1
+
+
+ amd64
+ 5895
+ deb
+ python3.10-minimal
+ Ubuntu
+ python3.10
+ 3.10.4-3ubuntu0.1
+ python
+ 3.10.4-3ubuntu0.1
+
+
+ all
+ 80
+ deb
+ readline-common
+ Ubuntu
+ readline
+ 8.1.2-1
+ utils
+ 8.1.2-1
+
+
+ amd64
+ 794
+ deb
+ rsync
+ Ubuntu
+
+ 3.2.7-0ubuntu0.22.04.2
+ net
+ 3.2.7-0ubuntu0.22.04.2
+
+
+ amd64
+ 1750
+ deb
+ rsyslog
+ Ubuntu
+
+ 8.2112.0-2ubuntu2.2
+ admin
+ 8.2112.0-2ubuntu2.2
+
+
+ amd64
+ 29084
+ deb
+ rudder-agent
+ Ubuntu
+
+ 7.2.5-ubuntu22.04
+ admin
+ 7.2.5-ubuntu22.04
+
+
+ all
+ 44
+ deb
+ run-one
+ Ubuntu
+
+ 1.17-0ubuntu1
+ admin
+ 1.17-0ubuntu1
+
+
+ amd64
+ 225
+ deb
+ sbsigntool
+ Ubuntu
+
+ 0.9.4-2ubuntu2
+ utils
+ 0.9.4-2ubuntu2
+
+
+ amd64
+ 1005
+ deb
+ screen
+ Ubuntu
+
+ 4.9.0-1
+ misc
+ 4.9.0-1
+
+
+ amd64
+ 29
+ deb
+ secureboot-db
+ Ubuntu
+
+ 1.8
+ utils
+ 1.8
+
+
+ amd64
+ 328
+ deb
+ sed
+ Ubuntu
+
+ 4.8-1ubuntu2
+ utils
+ 4.8-1ubuntu2
+
+
+ all
+ 59
+ deb
+ sensible-utils
+ Ubuntu
+
+ 0.0.17
+ utils
+ 0.0.17
+
+
+ amd64
+ 2789
+ deb
+ sg3-utils
+ Ubuntu
+
+ 1.46-1build1
+ admin
+ 1.46-1build1
+
+
+ all
+ 34
+ deb
+ sg3-utils-udev
+ Ubuntu
+ sg3-utils
+ 1.46-1build1
+ admin
+ 1.46-1build1
+
+
+ amd64
+ 2744
+ deb
+ shared-mime-info
+ Ubuntu
+
+ 2.1-2
+ misc
+ 2.1-2
+
+
+ amd64
+ 524
+ deb
+ sharutils
+ Ubuntu
+
+ 1:4.15.2-5build1
+ utils
+ 1:4.15.2-5build1
+
+
+ amd64
+ 87170
+ deb
+ snapd
+ Ubuntu
+
+ 2.55.5+22.04
+ devel
+ 2.55.5+22.04
+
+
+ all
+ 216
+ deb
+ software-properties-common
+ Ubuntu
+ software-properties
+ 0.99.22.2
+ admin
+ 0.99.22.2
+
+
+ amd64
+ 2708
+ deb
+ sosreport
+ Ubuntu
+
+ 4.3-1ubuntu2
+ admin
+ 4.3-1ubuntu2
+
+
+ amd64
+ 414
+ deb
+ squashfs-tools
+ Ubuntu
+
+ 1:4.5-3build1
+ kernel
+ 1:4.5-3build1
+
+
+ all
+ 53
+ deb
+ ssh-import-id
+ Ubuntu
+
+ 5.11-0ubuntu1
+ misc
+ 5.11-0ubuntu1
+
+
+ amd64
+ 2000
+ deb
+ strace
+ Ubuntu
+
+ 5.16-0ubuntu3
+ utils
+ 5.16-0ubuntu3
+
+
+ amd64
+ 2504
+ deb
+ sudo
+ Ubuntu
+
+ 1.9.9-1ubuntu2
+ admin
+ 1.9.9-1ubuntu2
+
+
+ amd64
+ 16288
+ deb
+ systemd
+ Ubuntu
+
+ 249.11-0ubuntu3.4
+ admin
+ 249.11-0ubuntu3.4
+
+
+ amd64
+ 195
+ deb
+ systemd-sysv
+ Ubuntu
+ systemd
+ 249.11-0ubuntu3.4
+ admin
+ 249.11-0ubuntu3.4
+
+
+ amd64
+ 266
+ deb
+ systemd-timesyncd
+ Ubuntu
+ systemd
+ 249.11-0ubuntu3.4
+ admin
+ 249.11-0ubuntu3.4
+
+
+ amd64
+ 83
+ deb
+ sysvinit-utils
+ Ubuntu
+ sysvinit
+ 3.01-1ubuntu1
+ admin
+ 3.01-1ubuntu1
+
+
+ amd64
+ 956
+ deb
+ tar
+ Ubuntu
+
+ 1.34+dfsg-1build3
+ utils
+ 1.34+dfsg-1build3
+
+
+ amd64
+ 22
+ deb
+ tcl
+ Ubuntu
+ tcltk-defaults
+ 8.6.11+1build2
+ interpreters
+ 8.6.11+1build2
+
+
+ amd64
+ 49
+ deb
+ tcl8.6
+ Ubuntu
+
+ 8.6.12+dfsg-1build1
+ interpreters
+ 8.6.12+dfsg-1build1
+
+
+ amd64
+ 1374
+ deb
+ tcpdump
+ Ubuntu
+
+ 4.99.1-3build2
+ net
+ 4.99.1-3build2
+
+
+ amd64
+ 154
+ deb
+ telnet
+ Ubuntu
+ netkit-telnet
+ 0.17-44build1
+ net
+ 0.17-44build1
+
+
+ amd64
+ 1421
+ deb
+ thin-provisioning-tools
+ Ubuntu
+
+ 0.9.0-2ubuntu1
+ admin
+ 0.9.0-2ubuntu1
+
+
+ amd64
+ 126
+ deb
+ time
+ Ubuntu
+
+ 1.9-0.1build2
+ utils
+ 1.9-0.1build2
+
+
+ amd64
+ 1026
+ deb
+ tmux
+ Ubuntu
+
+ 3.2a-4build1
+ admin
+ 3.2a-4build1
+
+
+ amd64
+ 231
+ deb
+ tnftp
+ Ubuntu
+
+ 20210827-4build1
+ net
+ 20210827-4build1
+
+
+ all
+ 18
+ deb
+ tpm-udev
+ Ubuntu
+
+ 0.6
+ admin
+ 0.6
+
+
+ amd64
+ 113
+ deb
+ tree
+ Ubuntu
+
+ 2.0.2-1
+ utils
+ 2.0.2-1
+
+
+ all
+ 3833
+ deb
+ tzdata
+ Ubuntu
+
+ 2022a-0ubuntu1
+ localization
+ 2022a-0ubuntu1
+
+
+ amd64
+ 2515
+ deb
+ ubuntu-advantage-tools
+ Ubuntu
+
+ 27.9~22.04.1
+ misc
+ 27.9~22.04.1
+
+
+ all
+ 41
+ deb
+ ubuntu-keyring
+ Ubuntu
+
+ 2021.03.26
+ misc
+ 2021.03.26
+
+
+ amd64
+ 53
+ deb
+ ubuntu-minimal
+ Ubuntu
+ ubuntu-meta
+ 1.481
+ metapackages
+ 1.481
+
+
+ all
+ 340
+ deb
+ ubuntu-release-upgrader-core
+ Ubuntu
+ ubuntu-release-upgrader
+ 1:22.04.11
+ admin
+ 1:22.04.11
+
+
+ amd64
+ 53
+ deb
+ ubuntu-server
+ Ubuntu
+ ubuntu-meta
+ 1.481
+ metapackages
+ 1.481
+
+
+ amd64
+ 53
+ deb
+ ubuntu-standard
+ Ubuntu
+ ubuntu-meta
+ 1.481
+ metapackages
+ 1.481
+
+
+ all
+ 232
+ deb
+ ucf
+ Ubuntu
+
+ 3.0043
+ utils
+ 3.0043
+
+
+ amd64
+ 9497
+ deb
+ udev
+ Ubuntu
+ systemd
+ 249.11-0ubuntu3.4
+ admin
+ 249.11-0ubuntu3.4
+
+
+ amd64
+ 1176
+ deb
+ udisks2
+ Ubuntu
+
+ 2.9.4-1ubuntu2
+ admin
+ 2.9.4-1ubuntu2
+
+
+ all
+ 830
+ deb
+ ufw
+ Ubuntu
+
+ 0.36.1-4build1
+ admin
+ 0.36.1-4build1
+
+
+ all
+ 436
+ deb
+ unattended-upgrades
+ Ubuntu
+
+ 2.8ubuntu1
+ admin
+ 2.8ubuntu1
+
+
+ all
+ 192
+ deb
+ update-manager-core
+ Ubuntu
+ update-manager
+ 1:22.04.9
+ admin
+ 1:22.04.9
+
+
+ all
+ 1137
+ deb
+ update-notifier-common
+ Ubuntu
+ update-notifier
+ 3.192.54
+ gnome
+ 3.192.54
+
+
+ amd64
+ 139
+ deb
+ usb-modeswitch
+ Ubuntu
+
+ 2.6.1-3ubuntu2
+ comm
+ 2.6.1-3ubuntu2
+
+
+ all
+ 94
+ deb
+ usb-modeswitch-data
+ Ubuntu
+
+ 20191128-4
+ comm
+ 20191128-4
+
+
+ all
+ 715
+ deb
+ usb.ids
+ Ubuntu
+
+ 2022.04.02-1
+ admin
+ 2022.04.02-1
+
+
+ amd64
+ 325
+ deb
+ usbutils
+ Ubuntu
+
+ 1:014-1build1
+ utils
+ 1:014-1build1
+
+
+ all
+ 200
+ deb
+ usrmerge
+ Ubuntu
+
+ 25ubuntu2
+ admin
+ 25ubuntu2
+
+
+ amd64
+ 3399
+ deb
+ util-linux
+ Ubuntu
+
+ 2.37.2-4ubuntu3
+ utils
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 197
+ deb
+ uuid-runtime
+ Ubuntu
+ util-linux
+ 2.37.2-4ubuntu3
+ utils
+ 2.37.2-4ubuntu3
+
+
+ amd64
+ 3918
+ deb
+ vim
+ Ubuntu
+
+ 2:8.2.3995-1ubuntu2.4
+ editors
+ 2:8.2.3995-1ubuntu2.4
+
+
+ all
+ 377
+ deb
+ vim-common
+ Ubuntu
+ vim
+ 2:8.2.3995-1ubuntu2.4
+ editors
+ 2:8.2.3995-1ubuntu2.4
+
+
+ all
+ 32778
+ deb
+ vim-runtime
+ Ubuntu
+ vim
+ 2:8.2.3995-1ubuntu2.4
+ editors
+ 2:8.2.3995-1ubuntu2.4
+
+
+ amd64
+ 1719
+ deb
+ vim-tiny
+ Ubuntu
+ vim
+ 2:8.2.3995-1ubuntu2.4
+ editors
+ 2:8.2.3995-1ubuntu2.4
+
+
+ amd64
+ 984
+ deb
+ wget
+ Ubuntu
+
+ 1.21.2-2ubuntu1
+ web
+ 1.21.2-2ubuntu1
+
+
+ amd64
+ 72
+ deb
+ whiptail
+ Ubuntu
+ newt
+ 0.52.21-5ubuntu2
+ utils
+ 0.52.21-5ubuntu2
+
+
+ amd64
+ 77
+ deb
+ xauth
+ Ubuntu
+
+ 1:1.1-1build2
+ x11
+ 1:1.1-1build2
+
+
+ amd64
+ 542
+ deb
+ xdg-user-dirs
+ Ubuntu
+
+ 0.17-2ubuntu4
+ utils
+ 0.17-2ubuntu4
+
+
+ amd64
+ 2784
+ deb
+ xfsprogs
+ Ubuntu
+
+ 5.13.0-1ubuntu2
+ admin
+ 5.13.0-1ubuntu2
+
+
+ all
+ 4236
+ deb
+ xkb-data
+ Ubuntu
+ xkeyboard-config
+ 2.33-1
+ x11
+ 2.33-1
+
+
+ amd64
+ 274
+ deb
+ xxd
+ Ubuntu
+ vim
+ 2:8.2.3995-1ubuntu2
+ editors
+ 2:8.2.3995-1ubuntu2
+
+
+ amd64
+ 372
+ deb
+ xz-utils
+ Ubuntu
+
+ 5.2.5-2ubuntu1
+ utils
+ 5.2.5-2ubuntu1
+
+
+ amd64
+ 30
+ deb
+ zerofree
+ Ubuntu
+
+ 1.1.1-1build3
+ admin
+ 1.1.1-1build3
+
+
+ amd64
+ 164
+ deb
+ zlib1g
+ Ubuntu
+ zlib
+ 1:1.2.11.dfsg-2ubuntu9
+ libs
+ 1:1.2.11.dfsg-2ubuntu9
+
+
+ amd64
+ 2468
+ deb
+ zsh
+ Ubuntu
+
+ 5.8.1-1
+ shells
+ 5.8.1-1
+
+
+ all
+ 15293
+ deb
+ zsh-common
+ Ubuntu
+ zsh
+ 5.8.1-1
+ shells
+ 5.8.1-1
+
+
+ amd64
+ 1655
+ deb
+ zstd
+ Ubuntu
+ libzstd
+ 1.4.8+dfsg-3build1
+ utils
+ 1.4.8+dfsg-3build1
+
+
+ all
+ apt-get
+ none
+ motd-news-config
+ Ubuntu:22.04/jammy-updates
+ 12ubuntu4.3
+
+
+ amd64
+ apt-get
+ none
+ libc6
+ Ubuntu:22.04/jammy-updates
+ 2.35-0ubuntu3.1
+
+
+ amd64
+ apt-get
+ none
+ base-files
+ Ubuntu:22.04/jammy-updates
+ 12ubuntu4.3
+
+
+ amd64
+ apt-get
+ none
+ gzip
+ Ubuntu:22.04/jammy-updates
+ 1.10-4ubuntu4.1
+
+
+ amd64
+ apt-get
+ none
+ libc-bin
+ Ubuntu:22.04/jammy-updates
+ 2.35-0ubuntu3.1
+
+
+ amd64
+ apt-get
+ none
+ libapparmor1
+ Ubuntu:22.04/jammy-updates
+ 3.0.4-2ubuntu2.2
+
+
+ amd64
+ apt-get
+ none
+ libcryptsetup12
+ Ubuntu:22.04/jammy-updates
+ 2:2.4.3-1ubuntu1.1
+
+
+ amd64
+ apt-get
+ none
+ libapt-pkg6.0
+ Ubuntu:22.04/jammy-updates
+ 2.4.8
+
+
+ amd64
+ apt-get
+ none
+ apt
+ Ubuntu:22.04/jammy-updates
+ 2.4.8
+
+
+ amd64
+ apt-get
+ none
+ apt-utils
+ Ubuntu:22.04/jammy-updates
+ 2.4.8
+
+
+ all
+ apt-get
+ none
+ python3-distutils
+ Ubuntu:22.04/jammy-updates
+ 3.10.6-1~22.04
+
+
+ all
+ apt-get
+ none
+ python3-lib2to3
+ Ubuntu:22.04/jammy-updates
+ 3.10.6-1~22.04
+
+
+ amd64
+ apt-get
+ none
+ python3-minimal
+ Ubuntu:22.04/jammy-updates
+ 3.10.6-1~22.04
+
+
+ amd64
+ apt-get
+ none
+ python3
+ Ubuntu:22.04/jammy-updates
+ 3.10.6-1~22.04
+
+
+ amd64
+ apt-get
+ none
+ libpython3-stdlib
+ Ubuntu:22.04/jammy-updates
+ 3.10.6-1~22.04
+
+
+ amd64
+ apt-get
+ none
+ openssh-sftp-server
+ Ubuntu:22.04/jammy-updates
+ 1:8.9p1-3ubuntu0.1
+
+
+ amd64
+ apt-get
+ none
+ openssh-server
+ Ubuntu:22.04/jammy-updates
+ 1:8.9p1-3ubuntu0.1
+
+
+ amd64
+ apt-get
+ none
+ openssh-client
+ Ubuntu:22.04/jammy-updates
+ 1:8.9p1-3ubuntu0.1
+
+
+ all
+ apt-get
+ none
+ python-apt-common
+ Ubuntu:22.04/jammy-updates
+ 2.4.0ubuntu1
+
+
+ all
+ apt-get
+ none
+ distro-info-data
+ Ubuntu:22.04/jammy-updates
+ 0.52ubuntu0.2
+
+
+ amd64
+ apt-get
+ none
+ python3-apt
+ Ubuntu:22.04/jammy-updates
+ 2.4.0ubuntu1
+
+
+ all
+ apt-get
+ none
+ ubuntu-release-upgrader-core
+ Ubuntu:22.04/jammy-updates
+ 1:22.04.16
+
+
+ all
+ apt-get
+ none
+ python3-distupgrade
+ Ubuntu:22.04/jammy-updates
+ 1:22.04.16
+
+
+ all
+ apt-get
+ none
+ update-manager-core
+ Ubuntu:22.04/jammy-updates
+ 1:22.04.10
+
+
+ all
+ apt-get
+ none
+ python3-update-manager
+ Ubuntu:22.04/jammy-updates
+ 1:22.04.10
+
+
+ amd64
+ apt-get
+ none
+ ubuntu-advantage-tools
+ Ubuntu:22.04/jammy-updates
+ 27.13.6~22.04.1
+
+
+ all
+ apt-get
+ none
+ update-notifier-common
+ Ubuntu:22.04/jammy-updates
+ 3.192.54.6
+
+
+ all
+ apt-get
+ none
+ libdrm-common
+ Ubuntu:22.04/jammy-updates
+ 2.4.113-2~ubuntu0.22.04.1
+
+
+ amd64
+ apt-get
+ none
+ libdrm2
+ Ubuntu:22.04/jammy-updates
+ 2.4.113-2~ubuntu0.22.04.1
+
+
+ all
+ apt-get
+ none
+ libglib2.0-data
+ Ubuntu:22.04/jammy-updates
+ 2.72.4-0ubuntu1
+
+
+ amd64
+ apt-get
+ none
+ libglib2.0-bin
+ Ubuntu:22.04/jammy-updates
+ 2.72.4-0ubuntu1
+
+
+ amd64
+ apt-get
+ none
+ libglib2.0-0
+ Ubuntu:22.04/jammy-updates
+ 2.72.4-0ubuntu1
+
+
+ amd64
+ apt-get
+ none
+ open-vm-tools
+ Ubuntu:22.04/jammy-updates
+ 2:12.1.0-1~ubuntu0.22.04.1
+
+
+ amd64
+ apt-get
+ none
+ isc-dhcp-client
+ Ubuntu:22.04/jammy-updates
+ 4.4.1-2.3ubuntu2.4
+
+
+ amd64
+ apt-get
+ none
+ isc-dhcp-common
+ Ubuntu:22.04/jammy-updates
+ 4.4.1-2.3ubuntu2.4
+
+
+ amd64
+ apt-get
+ none
+ kbd
+ Ubuntu:22.04/jammy-updates
+ 2.3.0-3ubuntu4.22.04
+
+
+ all
+ apt-get
+ none
+ initramfs-tools
+ Ubuntu:22.04/jammy-updates
+ 0.140ubuntu13.1
+
+
+ all
+ apt-get
+ none
+ initramfs-tools-core
+ Ubuntu:22.04/jammy-updates
+ 0.140ubuntu13.1
+
+
+ amd64
+ apt-get
+ none
+ initramfs-tools-bin
+ Ubuntu:22.04/jammy-updates
+ 0.140ubuntu13.1
+
+
+ amd64
+ apt-get
+ none
+ netplan.io
+ Ubuntu:22.04/jammy-updates
+ 0.105-0ubuntu2~22.04.3
+
+
+ amd64
+ apt-get
+ none
+ libnetplan0
+ Ubuntu:22.04/jammy-updates
+ 0.105-0ubuntu2~22.04.3
+
+
+ all
+ apt-get
+ none
+ locales
+ Ubuntu:22.04/jammy-updates
+ 2.35-0ubuntu3.1
+
+
+ amd64
+ apt-get
+ none
+ python3-gi
+ Ubuntu:22.04/jammy-updates
+ 3.42.1-0ubuntu1
+
+
+ amd64
+ apt-get
+ none
+ apparmor
+ Ubuntu:22.04/jammy-updates
+ 3.0.4-2ubuntu2.2
+
+
+ amd64
+ apt-get
+ none
+ bind9-dnsutils
+ Ubuntu:22.04/jammy-updates
+ 1:9.18.12-0ubuntu0.22.04.1
+
+
+ amd64
+ apt-get
+ none
+ bind9-host
+ Ubuntu:22.04/jammy-updates
+ 1:9.18.12-0ubuntu0.22.04.1
+
+
+ amd64
+ apt-get
+ none
+ bind9-libs
+ Ubuntu:22.04/jammy-updates
+ 1:9.18.12-0ubuntu0.22.04.1
+
+
+ amd64
+ apt-get
+ none
+ dmidecode
+ Ubuntu:22.04/jammy-updates
+ 3.3-3ubuntu0.1
+
+
+ amd64
+ apt-get
+ none
+ nftables
+ Ubuntu:22.04/jammy-updates
+ 1.0.2-1ubuntu3
+
+
+ amd64
+ apt-get
+ none
+ libnftables1
+ Ubuntu:22.04/jammy-updates
+ 1.0.2-1ubuntu3
+
+
+ amd64
+ apt-get
+ none
+ python3-gdbm
+ Ubuntu:22.04/jammy-updates
+ 3.10.6-1~22.04
+
+
+ amd64
+ apt-get
+ none
+ tcpdump
+ Ubuntu:22.04/jammy-updates
+ 4.99.1-3ubuntu0.1
+
+
+ amd64
+ apt-get
+ none
+ grub2-common
+ Ubuntu:22.04/jammy-updates
+ 2.06-2ubuntu7.1
+
+
+ amd64
+ apt-get
+ none
+ grub-pc
+ Ubuntu:22.04/jammy-updates
+ 2.06-2ubuntu7.1
+
+
+ amd64
+ apt-get
+ none
+ grub-pc-bin
+ Ubuntu:22.04/jammy-updates
+ 2.06-2ubuntu7.1
+
+
+ amd64
+ apt-get
+ none
+ grub-common
+ Ubuntu:22.04/jammy-updates
+ 2.06-2ubuntu7.1
+
+
+ all
+ apt-get
+ none
+ python3-problem-report
+ Ubuntu:22.04/jammy-updates
+ 2.20.11-0ubuntu82.3
+
+
+ all
+ apt-get
+ none
+ python3-apport
+ Ubuntu:22.04/jammy-updates
+ 2.20.11-0ubuntu82.3
+
+
+ all
+ apt-get
+ none
+ apport
+ Ubuntu:22.04/jammy-updates
+ 2.20.11-0ubuntu82.3
+
+
+ all
+ apt-get
+ none
+ cryptsetup-initramfs
+ Ubuntu:22.04/jammy-updates
+ 2:2.4.3-1ubuntu1.1
+
+
+ amd64
+ apt-get
+ none
+ cryptsetup-bin
+ Ubuntu:22.04/jammy-updates
+ 2:2.4.3-1ubuntu1.1
+
+
+ amd64
+ apt-get
+ none
+ cryptsetup
+ Ubuntu:22.04/jammy-updates
+ 2:2.4.3-1ubuntu1.1
+
+
+ amd64
+ apt-get
+ none
+ fwupd
+ Ubuntu:22.04/jammy-updates
+ 1.7.9-1~22.04.1
+
+
+ amd64
+ apt-get
+ none
+ libfwupdplugin5
+ Ubuntu:22.04/jammy-updates
+ 1.7.9-1~22.04.1
+
+
+ amd64
+ apt-get
+ none
+ libfwupd2
+ Ubuntu:22.04/jammy-updates
+ 1.7.9-1~22.04.1
+
+
+ amd64
+ apt-get
+ none
+ libmbim-proxy
+ Ubuntu:22.04/jammy-updates
+ 1.28.0-1~ubuntu20.04.1
+
+
+ amd64
+ apt-get
+ none
+ libmbim-glib4
+ Ubuntu:22.04/jammy-updates
+ 1.28.0-1~ubuntu20.04.1
+
+
+ amd64
+ apt-get
+ none
+ libmm-glib0
+ Ubuntu:22.04/jammy-updates
+ 1.20.0-1~ubuntu22.04.1
+
+
+ amd64
+ apt-get
+ none
+ libqmi-proxy
+ Ubuntu:22.04/jammy-updates
+ 1.32.0-1ubuntu0.22.04.1
+
+
+ amd64
+ apt-get
+ none
+ libqmi-glib5
+ Ubuntu:22.04/jammy-updates
+ 1.32.0-1ubuntu0.22.04.1
+
+
+ amd64
+ apt-get
+ none
+ fwupd-signed
+ Ubuntu:22.04/jammy-updates
+ 1.51~22.04.1+1.2-3ubuntu0.2
+
+
+ amd64
+ apt-get
+ none
+ libgstreamer1.0-0
+ Ubuntu:22.04/jammy-updates
+ 1.20.3-0ubuntu1
+
+
+ all
+ apt-get
+ none
+ libldap-common
+ Ubuntu:22.04/jammy-updates
+ 2.5.14+dfsg-0ubuntu0.22.04.1
+
+
+ amd64
+ apt-get
+ none
+ libsasl2-modules-db
+ Ubuntu:22.04/jammy-updates
+ 2.1.27+dfsg2-3ubuntu1.2
+
+
+ amd64
+ apt-get
+ none
+ libsasl2-2
+ Ubuntu:22.04/jammy-updates
+ 2.1.27+dfsg2-3ubuntu1.2
+
+
+ amd64
+ apt-get
+ none
+ libsasl2-modules
+ Ubuntu:22.04/jammy-updates
+ 2.1.27+dfsg2-3ubuntu1.2
+
+
+ amd64
+ apt-get
+ none
+ modemmanager
+ Ubuntu:22.04/jammy-updates
+ 1.20.0-1~ubuntu22.04.1
+
+
+ all
+ apt-get
+ none
+ software-properties-common
+ Ubuntu:22.04/jammy-updates
+ 0.99.22.6
+
+
+ all
+ apt-get
+ none
+ python3-software-properties
+ Ubuntu:22.04/jammy-updates
+ 0.99.22.6
+
+
+ all
+ apt-get
+ none
+ python3-tz
+ Ubuntu:22.04/jammy-updates
+ 2022.1-1ubuntu0.22.04.0
+
+
+ amd64
+ apt-get
+ none
+ snapd
+ Ubuntu:22.04/jammy-updates
+ 2.58+22.04
+
+
+ all
+ apt-get
+ none
+ cloud-init
+ Ubuntu:22.04/jammy-updates
+ 23.1.1-0ubuntu0~22.04.1
+
+
+ rev 01
+ Intel Corporation 82801AA AC'97 Audio Controller
+ Multimedia audio controller
+
+
+ SCSI
+ 42949
+ 10
+ VBOX
+ HARDDISK
+ sda
+ a23d4170
+ disk
+
+
+ SCSI
+ 10
+ 10
+ VBOX
+ HARDDISK
+ sdb
+ disk
+
+
+ vagrant
+
+ FusionInventory-Agent_v2.4.3
+
+ Platform : linux localhost 4.19.0 1 smp debian 4.19.0 x86_64 gnulinux
+ Build date: Wed Feb 1 04:18:40 2023 GMT
+ FusionInventory
+ /usr/bin/perl
+ v5.34.0
+ /opt/rudder/bin/fusioninventory-agent
+ 2.4.3
+
+
+ node2-2023-04-03-11-35-07
+ INVENTORY
+
diff --git a/webapp/sources/rudder/rudder-core/src/test/resources/inventories/7.2/node2-86d9ec77-9db5-4ba3-bdca-f0baf3a5b477.ocs.sign b/webapp/sources/rudder/rudder-core/src/test/resources/inventories/7.2/node2-86d9ec77-9db5-4ba3-bdca-f0baf3a5b477.ocs.sign
new file mode 100644
index 00000000000..7ddf88b2c1a
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/resources/inventories/7.2/node2-86d9ec77-9db5-4ba3-bdca-f0baf3a5b477.ocs.sign
@@ -0,0 +1,6 @@
+header=rudder-signature-v1
+algorithm=sha512
+digest=2de834195f48cfe534ae419bdc2aa32d5f27f805f3149987f4f823574f1169d04712f65329ab336c5a13ddd86cd1d2d2250ec0c450ce7030c8d977f3473e823d185e43960eeb03a5db38ff991b98da53e6a103f3395177d699dd8517046eee27263a701049decfb0df4030c1112a7af9bbcc394420bf937c3a1adae4ee45ea3755af219538e1b5a89e7f25a43ad1e0f57ecc9865d6afaab09436265cd3c7d52e0800925d60b35d1b1d8efaf0c75f3b98d35a18647e8c52711c58f5633c1686242fc2a3563075443813341af05c2d0ce7f1e1281658d475ec1539352b77457f806fc0e9c8aa37a6ddd41028c41e75cb3d360955ae1a1468cc1857311e233d72f5f0f03da157bb3ad1df7818c379dce0b4178730a61c817f65323abc921fc605e69565e3bbdb7902804d890753734f38cec0f74be442dc9fa8c8091f4795889aaa2413669ee93ee7aa417343cfe04e531fd083f831f3ecd48d3bfe6691ff6f529576970e9664744d92db33c29d6e72f81d1f1f37c95a6c198bb6f61e5d9aa007daf1cbf3bdb7c3cf9cddb3630c9acf16a6466b9b833d2c1decab5b85925fd6ee52af7f27664f770c1ee4af86cfde30ef8f1356e696999b46ad21c724b5af3fca42613b954280a4b37f5e4b450f4758077642abee4d47c414f6ad794234ce71b4f0b245fdd9eadc999bb87a80d7e37509a3bc71c9c33cf20a3dabc727a04f31df5c
+hostname=node2
+keydate=2023-04-03 09:24:57.167078314 +0000
+keyid=B18F68CD
diff --git a/webapp/sources/rudder/rudder-core/src/test/resources/ldap-data/inventory-sample-data.ldif b/webapp/sources/rudder/rudder-core/src/test/resources/ldap-data/inventory-sample-data.ldif
index 14c8d25ffee..abc76f83a30 100644
--- a/webapp/sources/rudder/rudder-core/src/test/resources/ldap-data/inventory-sample-data.ldif
+++ b/webapp/sources/rudder/rudder-core/src/test/resources/ldap-data/inventory-sample-data.ldif
@@ -34,7 +34,7 @@ jsonNodeGroupQuery: {"select":"node","composition":"Or","where":[{"objectType":"
nodeId: node1
nodeId: node2
-dn: nodeGroupId=test-group-node22,groupCategoryId=GroupRoot,ou=Rudder,cn=rudder-configuration
+dn: nodeGroupId=test-group-node23,groupCategoryId=GroupRoot,ou=Rudder,cn=rudder-configuration
objectClass: nodeGroup
objectClass: top
cn: Only contains node2 and node3
@@ -233,7 +233,7 @@ localAdministratorAccountName: root
agentName: {"agentType":"Community","version":"4.1.8"}
policyServerId:root
nodeHostname: root.normation.com
-inventoryDate: 20130515123456.948Z
+inventoryDate: 20120515123456.948Z
publicKey: "test"
software: softwareId=soft0,ou=Software,ou=Inventories,cn=rudder-configuration
@@ -289,6 +289,7 @@ cn: has attributes
swap: 2878000000
ram: 100000000
nodeHostname: hasAttributes.normation.com
+inventoryDate: 20140515123456.948Z
description: #54-Ubuntu SMP Thu Dec 10 17:23:29 UTC 2009
localAccountName: francois.armand
localAccountName: nicolas.charles
@@ -327,6 +328,7 @@ localAdministratorAccountName: root
agentName: {"agentType":"cfengine-nova","version":"4.2.0-jessie0","securityToken":{"value":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA2AlhQKo0lMwsInTJzOIimehRQDgeO4SuAAfnpzxKRLsBLcGViW4n\nZ4VrfW+xNOWXKJpEDRaVX6C/JQoVsCrvsLcnVPz/qxLZTGQzsirDPJE5WX+kN+nz\nYoo2jM5VNoZEioh1I9854dvyI6qv7BbNWHxZ9k5VcTSs33YUowBsiB+54h4w5MFN\nibrq5UcGMxhQAmWGzlACLM7E7Bu70O4NXEAmBFnXh6Gni7KL5MAPKQ0vy3O0is8+\n8Z6uA3Mo1yOtSiH3AtgpsSuRdNQqyinqMXkpBzE4mrIiftEo/FF8j/xFQTtoI/mw\nMMedHfNOeDzTGMtVjiXwtSZq6LmjIRtIDQIDAQAB\n-----END RSA PUBLIC KEY-----","type":"publicKey"}}
policyServerId:root-policy-server
nodeHostname: node2.normation.com
+inventoryDate: 20150515123456.948Z
ipHostNumber: 192.168.56.102
ipHostNumber: 127.0.0.1
publicKey: "test"
@@ -347,6 +349,7 @@ localAdministratorAccountName: root
agentName: {"agentType":"cfengine-community","version":"4.2.0-jessie0","securityToken":{"value":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA2AlhQKo0lMwsInTJzOIimehRQDgeO4SuAAfnpzxKRLsBLcGViW4n\nZ4VrfW+xNOWXKJpEDRaVX6C/JQoVsCrvsLcnVPz/qxLZTGQzsirDPJE5WX+kN+nz\nYoo2jM5VNoZEioh1I9854dvyI6qv7BbNWHxZ9k5VcTSs33YUowBsiB+54h4w5MFN\nibrq5UcGMxhQAmWGzlACLM7E7Bu70O4NXEAmBFnXh6Gni7KL5MAPKQ0vy3O0is8+\n8Z6uA3Mo1yOtSiH3AtgpsSuRdNQqyinqMXkpBzE4mrIiftEo/FF8j/xFQTtoI/mw\nMMedHfNOeDzTGMtVjiXwtSZq6LmjIRtIDQIDAQAB\n-----END RSA PUBLIC KEY-----","type":"publicKey"}}
policyServerId:root-policy-server
nodeHostname: node3.normation.com
+inventoryDate: 20160515123456.948Z
ipHostNumber: 192.168.56.103
ipHostNumber: 127.0.0.1
publicKey: "test"
@@ -368,6 +371,7 @@ localAdministratorAccountName: root
agentName: {"agentType":"cfengine-community","version":"4.2.0-jessie0","securityToken":{"value":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA2AlhQKo0lMwsInTJzOIimehRQDgeO4SuAAfnpzxKRLsBLcGViW4n\nZ4VrfW+xNOWXKJpEDRaVX6C/JQoVsCrvsLcnVPz/qxLZTGQzsirDPJE5WX+kN+nz\nYoo2jM5VNoZEioh1I9854dvyI6qv7BbNWHxZ9k5VcTSs33YUowBsiB+54h4w5MFN\nibrq5UcGMxhQAmWGzlACLM7E7Bu70O4NXEAmBFnXh6Gni7KL5MAPKQ0vy3O0is8+\n8Z6uA3Mo1yOtSiH3AtgpsSuRdNQqyinqMXkpBzE4mrIiftEo/FF8j/xFQTtoI/mw\nMMedHfNOeDzTGMtVjiXwtSZq6LmjIRtIDQIDAQAB\n-----END RSA PUBLIC KEY-----","type":"publicKey","test":"dsc"}}
policyServerId:root-policy-server
nodeHostname: node4.normation.com
+inventoryDate: 20170515123456.948Z
ipHostNumber: 127.0.0.1
environmentVariable: {"name":"SHELL","value":"/bin/sh"}
@@ -386,6 +390,7 @@ localAdministratorAccountName: root
agentName: {"agentType":"dsc","version":"4.2-1.0","securityToken":{"value":"-----BEGIN CERTIFICATE-----\nMIIFTTCCAzWgAwIBAgIJAL4vbx1mfhs5MA0GCSqGSIb3DQEBCwUAMEcxDzANBgNV\nBAMMBkFHRU5UMjE0MDIGCgmSJomT8ixkAQEMJDM1NWVhY2QxLWU4YjAtNDg4OC1i\nZTFkLTUxMDQ3MTdkZTZjZjAeFw0xNzEwMDQxNjUyMTJaFw0yNzEwMDIxNjUyMTJa\nMEcxDzANBgNVBAMMBkFHRU5UMjE0MDIGCgmSJomT8ixkAQEMJDM1NWVhY2QxLWU4\nYjAtNDg4OC1iZTFkLTUxMDQ3MTdkZTZjZjCCAiIwDQYJKoZIhvcNAQEBBQADggIP\nADCCAgoCggIBALv6AoYF59+F/jGPQq074gf8HZwowysuT4uxFbFpkYBc6FvLuRrZ\nnz0lJZKZOSjWbhndKLFkKgsHBi35ESfblBf4lqBguyCPWKRyGApSJP0cElSNKJsi\nzd0qXorTKV0aEQod2TUjz35Vbl2rYPbt+vIGX0zK0cBhTiSJ8ONLMoCUxeqVXmqL\nSisD6LfR9NH+0+LZ4g0ueQzC+DJncb0wbH66vbg6soykQ2c1XljRgdJHrEgPy43x\nL6WvL6Sb4hlPs7yBHwWTGXsAKjs8kMBON9ijPnS30gQm5flqd8lFd/s1/7yYrXBl\n+e8cOVTorgL1biQb250MRaPp4PL3NvbpLtMaSK8aAdxQSFhMxvCHq+41VbjAkIPG\n9yekMqsbs22BZn4XoTbn5F71FGh39j6cI/BJYOI6sDmnVfWuMTwdGnzh43fNDpch\n7FeuDUTomFkJXFNMZuvuEhLtf39OIknzhszxXrsSG8VSHAS4GdRXSFu9bbtuLm2Q\ngiiVLkE69NUgM+XHM9XKiNY2oDNtVpVrRte4hdH7NgG2LgBs+bYNPI44po4AfGnc\n2ICzC1UXEnNpH0WGVZ4OtBKZlmHLC7RhCXOkTBOX29yBag4jIfDaIYNthDmSX3By\nVoh1/hLrXcTnIzMn31Ku3CKVbYeMBEzmZGLtDSQvoedAgv0VCgf8fRhZAgMBAAGj\nPDA6MAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgK0MB0GA1UdDgQWBBSaV2KqEPqU\n2xWF2ajE/h0a5fB7LzANBgkqhkiG9w0BAQsFAAOCAgEAV7/A+nlL/bWOd906t3QR\nt57hQfgBkNralQyNvsspFaDJM19G+Xi2yhW/Vq9tZNJ0FzbMwp2OwcADmDtnFG/Y\nBa83jU8Cxa/wvQO86KCNK4NlzGjcWNtwGM4135r7M8t3dvx+uXu+AYa96QrVLTUX\n4XsRJWTdf4Qe6zgKuaDfsEr0eDAo6UNe+ZQyPNJoqPERKTTcv8BDimAGCdO0ZAUy\nMd6Cu6WTpMrWvhr/YzvwQm9tJ7GvQoVd3HAyO/+6dZOqFoJmoI6NXB2thSEMJMQ7\nAiniqRO04opf56Z1K0RO8/ECsr81OL4R0j7Bx+SNVGQP+FDDUdiJPZp1SeQgrgSb\nihCQr8zWZtmXZE0UKIAHXsfCFCNr/t4yPgOsAlD1Cs0QRglXr/M15jmpmWlD7IiB\nbx0aOdwH99a2HK7d41v1yoZn4bKdgtbEaPHXViAPnFdcJPQ1+1hm8G2vbhYJgv1d\no+ZgEyZNfamAPCKKyy79JVPeas4alSnBw+RRRKxH4ZAr7E+Urml7JFmoiab0jjGY\nOjgEzRQUOiTdSNvpzJUz71KrQPgR0gIlsjnyu3QOoFXdVtg+MzLyOb4bCmo3mFL2\nsAhdducYbLhNS/IOunspkZJzfgRodgzOj1ZRlTJztP+sdd5M2rJy6awpWL4AwUMP\nyDa4p7g4y2ju9vIh+t4C8qk=\n-----END CERTIFICATE-----","type":"certificate"}}
policyServerId:root-policy-server
nodeHostname: node5.normation.com
+inventoryDate: 20180515123456.948Z
publicKey: "test"
dn: nodeId=node6,ou=Nodes,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration
@@ -403,6 +408,7 @@ localAdministratorAccountName: root
agentName: Community
policyServerId:root-policy-server
nodeHostname: node6.normation.com
+inventoryDate: 20190515123456.948Z
publicKey: "test"
dn: nodeId=node7,ou=Nodes,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration
@@ -421,6 +427,7 @@ localAdministratorAccountName: root
agentName: {"agentType":"cfengine-community","version":"4.2.0-jessie0","securityToken":{"value":"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEA2AlhQKo0lMwsInTJzOIimehRQDgeO4SuAAfnpzxKRLsBLcGViW4n\nZ4VrfW+xNOWXKJpEDRaVX6C/JQoVsCrvsLcnVPz/qxLZTGQzsirDPJE5WX+kN+nz\nYoo2jM5VNoZEioh1I9854dvyI6qv7BbNWHxZ9k5VcTSs33YUowBsiB+54h4w5MFN\nibrq5UcGMxhQAmWGzlACLM7E7Bu70O4NXEAmBFnXh6Gni7KL5MAPKQ0vy3O0is8+\n8Z6uA3Mo1yOtSiH3AtgpsSuRdNQqyinqMXkpBzE4mrIiftEo/FF8j/xFQTtoI/mw\nMMedHfNOeDzTGMtVjiXwtSZq6LmjIRtIDQIDAdsc\n-----END RSA PUBLIC KEY-----","type":"publicKey"}}
policyServerId:root-policy-server
nodeHostname: node7.normation.com
+inventoryDate: 20200515123456.948Z
publicKey: "test"
###################################################################################################
diff --git a/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/4d3a43bc-8508-46a2-92d7-cfe7320309a5.json_ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/4d3a43bc-8508-46a2-92d7-cfe7320309a5.json_
new file mode 100644
index 00000000000..e6f41d00e51
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/4d3a43bc-8508-46a2-92d7-cfe7320309a5.json_
@@ -0,0 +1,5876 @@
+{
+ "id" : "4d3a43bc-8508-46a2-92d7-cfe7320309a5",
+ "hostname" : "node1-overridden.rudder.local.override",
+ "os" : {
+ "type" : "Linux",
+ "name" : "Ubuntu",
+ "version" : "22.04",
+ "fullName" : "Ubuntu 22.04 LTS",
+ "kernelVersion" : "5.15.0-41-generic"
+ },
+ "rudderSettings" : {
+ "keyStatus" : "certified",
+ "reportingConfiguration" : {
+
+ },
+ "kind" : "node",
+ "status" : "pending",
+ "state" : "enabled",
+ "policyMode" : "default",
+ "policyServerId" : "root"
+ },
+ "rudderAgent" : {
+ "type" : "cfengine-community",
+ "user" : "root",
+ "version" : "7.2.0~rc1-ubuntu22.04",
+ "securityToken" : {
+ "kind" : "certificate",
+ "token" : "-----BEGIN CERTIFICATE-----\nMIIFqzCCA5OgAwIBAgIULhYaqmQZGDMab0FnySgkA3tCWpgwDQYJKoZIhvcNAQEL\nBQAwNjE0MDIGCgmSJomT8ixkAQEMJDRkM2E0M2JjLTg1MDgtNDZhMi05MmQ3LWNm\nZTczMjAzMDlhNTAeFw0yMjA5MDcwODUyNDdaFw0zMjA5MDQwODUyNDdaMDYxNDAy\nBgoJkiaJk/IsZAEBDCQ0ZDNhNDNiYy04NTA4LTQ2YTItOTJkNy1jZmU3MzIwMzA5\nYTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDD7/K+BfMAT4pwJf03\nHH9nTBXdDyibg4wbSy0zjiUrQ2Ri5tiwCsfUQtpkeHl3a643FBmHKE2oDvKOvFFV\nrBJWqtJBydVMwhBlDuDXR0WV9fPyIizIcAX/LBt1yPRSrtBkWQZ2IAN/lCADmtEe\ngkVyLiVcp/msMAdwm0QkDvB1o+g3USq0DFbwkQyopvTsYfGgcW/Rn9ifCyxq2rKe\nF+zbUIF7AK5dPjXyDFEEo80WUAZEkn3hQrEKYOCXkEiRLI6+KCArnv9jRop92dCg\nTjGIoMLtAQYb+txytf8/4oQDpCdEENvRJqQ2SVB1K1dtvLPXe7jQ2vK9/chtmFut\ndirZCOBRtbRV0eAZ7te4K0RC5L0R2EI7uowmhE5a8xwmWGH1U7BMVKR9B76iPdR3\nK9fXiPRUEBvs2N/06OBviY18VSR5k4sp9sEifAq3kgP/kvVKTA1dNavoB1gDH8KS\n+0Nnp9nA+AxMLyU5PFFvwSfgYZjp+sbDlFPrOgryVK0BLfGNdm27xftKW8yyzNPM\nbRirrnIngvh8wPHjDxC02/SJ7Pikx0ffOV09ee+Ik4dTbqj+bHy/z8rp0aAtLbZV\nNIGqvzCaByWWJhmYZ3FFwHo3LXUeonljQOYVl8kSEQkcE8b5X657fG3RTrLUE6DK\nobsJcr7JUwijGcB+tADQ+gngtQIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYD\nVR0OBBYEFChVcwIhgIr5vgRy8T8ol3MktitiMHEGA1UdIwRqMGiAFChVcwIhgIr5\nvgRy8T8ol3MktitioTqkODA2MTQwMgYKCZImiZPyLGQBAQwkNGQzYTQzYmMtODUw\nOC00NmEyLTkyZDctY2ZlNzMyMDMwOWE1ghQuFhqqZBkYMxpvQWfJKCQDe0JamDAL\nBgNVHQ8EBAMCArwwDQYJKoZIhvcNAQELBQADggIBAEzLJFdRib1gHQ3ZFU6YCeEX\nUYd8DBRAnPUdjSi/6+Qz9KhQdo5F+vngAdqWFggL2lv1yaahLKTYSdxibBpzX9ee\nH8mP+Gxt0su0xoAtuwkzvfg3G6BTtAXorVgsEfya3re3eShOuvXprzqi7Xsnbv9a\nUzJX4FCJJAXA+Qd/frzpw33cSa8M4SHn7xTo3c9ZgOs1zJ4BepFz/2MpKYY9RSfU\nDpeksnS+ij1BUcrScitwCVvmh0nFXIMWcnUREqxkRAPY1ln1ExAaSmYA0SKsUhoJ\nGx08WfzprwunlzZCA91gmNW+1WjMFr9Nonp6SlMXH0ZrBm+K+zNm6+aAnZdN2ttG\nPmmqotUbbCG737HY8VxE27rgZ8SYidprqvuPtz4WaQPg+4JPeCzOqLWfcX3nfmJM\nkO7nfL0DCqDaZgW+O3FCSFfXnb8TQnMgJZlnakI/w+mzx26YkxtZ2xgWi7yLyBk6\nJDeC5KwgyP+nJpgSOIwGNf4ZaOEYtg3DeFjXmFi5N4Vtq98819cTp3QL+RZG4cVs\nDwFR0T3KjB6QWcvSB2QpQUvdOITtUsACP/zk73y7RwZVQT0DHVWVw6eHhGH6LpKI\nY8skyhHnHUjG1A9eOvk4ZQGPedL9cG0af7TzmaUwFg5fGN594lv79o7iGT1srNBp\n2A/Eff5z/oWypwrLbjiI\n-----END CERTIFICATE-----"
+ },
+ "capabilities" : [
+ "jq",
+ "yaml",
+ "cfengine",
+ "curl",
+ "acl",
+ "xml",
+ "http_reporting"
+ ]
+ },
+ "properties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local",
+ "provider" : "inventory"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override",
+ "provider" : "inventory"
+ }
+ ],
+ "lastInventoryDate" : "2022-09-07T17:56:55Z",
+ "inventoryReceivedDate" : "2022-09-07T17:56:55Z",
+ "ipAddresses" : [
+ "192.168.32.4",
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1",
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "timezone" : {
+ "name" : "UTC",
+ "offset" : "+0000"
+ },
+ "machine" : {
+ "id" : "a9445c97-2640-5736-961f-514e9a34940a",
+ "provider" : "vbox",
+ "systemSerial" : "28eba2ed-b589-ec45-bae4-d33155c8288d",
+ "manufacturer" : "innotek GmbH"
+ },
+ "ram" : 489684992,
+ "archDescription" : "x86_64",
+ "accounts" : [
+ "_apt",
+ "backup",
+ "bin",
+ "daemon",
+ "games",
+ "gnats",
+ "irc",
+ "landscape",
+ "list",
+ "lp",
+ "lxd",
+ "mail",
+ "man",
+ "messagebus",
+ "news",
+ "nobody",
+ "pollinate",
+ "proxy",
+ "root",
+ "sshd",
+ "sync",
+ "sys",
+ "syslog",
+ "systemd-network",
+ "systemd-resolve",
+ "systemd-timesync",
+ "tcpdump",
+ "tss",
+ "ubuntu",
+ "uucp",
+ "uuidd",
+ "vagrant",
+ "www-data"
+ ],
+ "bios" : [
+ {
+ "name" : "VirtualBox",
+ "version" : "VirtualBox",
+ "editor" : "innotek GmbH",
+ "releaseDate" : "2006-11-30T23:00:00Z",
+ "quantity" : 1
+ }
+ ],
+ "controllers" : [
+ {
+ "name" : "440FX - 82441FX PMC [Natoma]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Host bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI",
+ "manufacturer" : "LSI Logic / Symbios Logic",
+ "cType" : "SCSI storage controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 ACPI",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 IDE",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "IDE interface",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371SB PIIX3 ISA [Natoma/Triton II]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "ISA bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82540EM Gigabit Ethernet Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Ethernet controller",
+ "quantity" : 2
+ },
+ {
+ "name" : "82801AA AC'97 Audio Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Multimedia audio controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Graphics Adapter",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "VGA compatible controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Guest Service",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "System peripheral",
+ "quantity" : 1
+ }
+ ],
+ "customProperties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override"
+ }
+ ],
+ "environmentVariables" : [
+ ["BASEDIR", "/opt/rudder/share/commands"],
+ ["DEBIAN_FRONTEND", "noninteractive"],
+ ["HOME", "/root"],
+ ["LESSCLOSE", "/usr/bin/lesspipe %s %s"],
+ ["LESSOPEN", "| /usr/bin/lesspipe %s"],
+ ["LOGNAME", "root"],
+ ["LS_COLORS", "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:"],
+ ["MAIL", "/var/mail/root"],
+ ["PATH", "/opt/rudder/bin:/usr/gnu/bin:/opt/rudder/bin:/usr/gnu/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin:/usr/sbin:/sbin:/usr/sbin"],
+ ["PWD", "/var/rudder"],
+ ["RUDDER_BIN", "/usr/bin/rudder"],
+ ["SHELL", "/bin/bash"],
+ ["SHLVL", "1"],
+ ["SUDO_COMMAND", "/usr/bin/su"],
+ ["SUDO_GID", "1000"],
+ ["SUDO_UID", "1000"],
+ ["SUDO_USER", "vagrant"],
+ ["TERM", "xterm"],
+ ["USER", "root"],
+ ["_", "/usr/bin/rudder"]
+ ],
+ "fileSystems" : [
+ {
+ "mountPoint" : "/",
+ "name" : "ext4",
+ "freeSpace" : 39669727232,
+ "totalSpace" : 41555066880
+ },
+ {
+ "mountPoint" : "/vagrant",
+ "name" : "vboxsf",
+ "freeSpace" : 91722088448,
+ "totalSpace" : 397236240384
+ }
+ ],
+ "inputs" : [],
+ "localGroups" : [],
+ "localUsers" : [],
+ "logicalVolumes" : [],
+ "memories" : [],
+ "networks" : [
+ {
+ "name" : "enp0s3",
+ "ifAddresses" : [
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "ifGateway" : [
+ "10.0.2.2"
+ ],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "10.0.2.0"
+ ],
+ "macAddress" : "02:e5:1b:70:f4:63",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "enp0s8",
+ "ifAddresses" : [
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "192.168.32.4"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "192.168.32.0"
+ ],
+ "macAddress" : "08:00:27:94:72:e8",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "lo",
+ "ifAddresses" : [
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "fff0:0:0:0:0:0:0:0",
+ "255.0.0.0"
+ ],
+ "ifSubnet" : [
+ "0:0:0:0:0:0:0:0",
+ "127.0.0.0"
+ ],
+ "macAddress" : "00:00:00:00:00:00",
+ "status" : "Up",
+ "ifType" : "loopback"
+ }
+ ],
+ "physicalVolumes" : [],
+ "ports" : [],
+ "processes" : [
+ {
+ "pid" : 24832,
+ "commandName" : "(sd-pam)",
+ "cpuUsage" : 0.0,
+ "memory" : 1.0,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 170396.0
+ },
+ {
+ "pid" : 24915,
+ "commandName" : "-bash",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/0",
+ "user" : "vagrant",
+ "virtualMemory" : 9148.0
+ },
+ {
+ "pid" : 50026,
+ "commandName" : "/bin/sh /opt/rudder/bin/rudder-perl -I /opt/rudder/lib/perl5 /opt/rudder/bin/fusioninventory-agent --config=none --no-task=Deploy --local=/var/rudder/tmp/inventory/node1-4d3a43bc-8508-46a2-92d7-cfe7320309a5.ocs",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 50022,
+ "commandName" : "/bin/sh /opt/rudder/bin/run-inventory --local=/var/rudder/tmp/inventory/node1-4d3a43bc-8508-46a2-92d7-cfe7320309a5.ocs",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49923,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/../lib/timestamp",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49785,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-inventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.3,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49925,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49825,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.4,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 24830,
+ "commandName" : "/lib/systemd/systemd --user",
+ "cpuUsage" : 0.0,
+ "memory" : 1.5,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 17080.0
+ },
+ {
+ "pid" : 339,
+ "commandName" : "/lib/systemd/systemd-journald",
+ "cpuUsage" : 0.0,
+ "memory" : 1.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 48364.0
+ },
+ {
+ "pid" : 683,
+ "commandName" : "/lib/systemd/systemd-logind",
+ "cpuUsage" : 0.0,
+ "memory" : 1.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 23720.0
+ },
+ {
+ "pid" : 1628,
+ "commandName" : "/lib/systemd/systemd-networkd",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:44",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 16248.0
+ },
+ {
+ "pid" : 558,
+ "commandName" : "/lib/systemd/systemd-resolved",
+ "cpuUsage" : 0.0,
+ "memory" : 1.7,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 25392.0
+ },
+ {
+ "pid" : 411,
+ "commandName" : "/lib/systemd/systemd-timesyncd",
+ "cpuUsage" : 0.0,
+ "memory" : 0.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 89352.0
+ },
+ {
+ "pid" : 385,
+ "commandName" : "/lib/systemd/systemd-udevd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.2,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 23304.0
+ },
+ {
+ "pid" : 49921,
+ "commandName" : "/opt/rudder/bin/cf-agent -I -D info -Cnever -K -b doInventory -D force_inventory",
+ "cpuUsage" : 55.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 74680.0
+ },
+ {
+ "pid" : 4493,
+ "commandName" : "/opt/rudder/bin/cf-execd --no-fork",
+ "cpuUsage" : 0.0,
+ "memory" : 2.8,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 33460.0
+ },
+ {
+ "pid" : 4495,
+ "commandName" : "/opt/rudder/bin/cf-serverd --graceful-detach=600 --no-fork --inform",
+ "cpuUsage" : 0.0,
+ "memory" : 2.6,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 33480.0
+ },
+ {
+ "pid" : 746,
+ "commandName" : "/sbin/agetty -o -p -- \\u --keep-baud 115200,57600,38400,9600 ttyS0 vt220",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "ttyS0",
+ "user" : "root",
+ "virtualMemory" : 6216.0
+ },
+ {
+ "pid" : 757,
+ "commandName" : "/sbin/agetty -o -p -- \\u --noclear tty1 linux",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "tty1",
+ "user" : "root",
+ "virtualMemory" : 6172.0
+ },
+ {
+ "pid" : 1,
+ "commandName" : "/sbin/init",
+ "cpuUsage" : 0.0,
+ "memory" : 2.2,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 167620.0
+ },
+ {
+ "pid" : 381,
+ "commandName" : "/sbin/multipathd -d -s",
+ "cpuUsage" : 0.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 354888.0
+ },
+ {
+ "pid" : 674,
+ "commandName" : "/usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers",
+ "cpuUsage" : 0.0,
+ "memory" : 2.3,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 32992.0
+ },
+ {
+ "pid" : 773,
+ "commandName" : "/usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal",
+ "cpuUsage" : 0.0,
+ "memory" : 2.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 110088.0
+ },
+ {
+ "pid" : 681,
+ "commandName" : "/usr/lib/snapd/snapd",
+ "cpuUsage" : 0.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 743408.0
+ },
+ {
+ "pid" : 2287,
+ "commandName" : "/usr/libexec/packagekitd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.8,
+ "started" : "2022-09-07 11:44",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 295940.0
+ },
+ {
+ "pid" : 675,
+ "commandName" : "/usr/libexec/polkitd --no-debug",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 234484.0
+ },
+ {
+ "pid" : 689,
+ "commandName" : "/usr/libexec/udisks2/udisksd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 392700.0
+ },
+ {
+ "pid" : 732,
+ "commandName" : "/usr/sbin/ModemManager",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 316932.0
+ },
+ {
+ "pid" : 665,
+ "commandName" : "/usr/sbin/cron -f -P",
+ "cpuUsage" : 0.0,
+ "memory" : 0.5,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 7284.0
+ },
+ {
+ "pid" : 680,
+ "commandName" : "/usr/sbin/rsyslogd -n -iNONE",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "syslog",
+ "virtualMemory" : 222400.0
+ },
+ {
+ "pid" : 666,
+ "commandName" : "@dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "message+",
+ "virtualMemory" : 8884.0
+ },
+ {
+ "pid" : 87,
+ "commandName" : "[acpi_thermal_pm]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5965,
+ "commandName" : "[arc_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5964,
+ "commandName" : "[arc_prune]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5966,
+ "commandName" : "[arc_reap]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 75,
+ "commandName" : "[ata_sff]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 73,
+ "commandName" : "[blkcg_punt_bio]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 115,
+ "commandName" : "[charger_manager]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 17,
+ "commandName" : "[cpuhp/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 153,
+ "commandName" : "[cryptd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5967,
+ "commandName" : "[dbu_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5968,
+ "commandName" : "[dbuf_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 78,
+ "commandName" : "[devfreq_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5911,
+ "commandName" : "[dio/sda1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 84,
+ "commandName" : "[ecryptfs-kthrea]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 77,
+ "commandName" : "[edac-poller]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 267,
+ "commandName" : "[ext4-rsv-conver]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 16,
+ "commandName" : "[idle_inject/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 19,
+ "commandName" : "[inet_frag_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 362,
+ "commandName" : "[ipmi-msghandler]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 96,
+ "commandName" : "[ipv6_addrconf]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 266,
+ "commandName" : "[jbd2/sda1-8]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 372,
+ "commandName" : "[kaluad]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 20,
+ "commandName" : "[kauditd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 72,
+ "commandName" : "[kblockd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 24,
+ "commandName" : "[kcompactd0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 18,
+ "commandName" : "[kdevtmpfs]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 21,
+ "commandName" : "[khungtaskd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 71,
+ "commandName" : "[kintegrityd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 379,
+ "commandName" : "[kmpath_handlerd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 374,
+ "commandName" : "[kmpath_rdacd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 376,
+ "commandName" : "[kmpathd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 25,
+ "commandName" : "[ksmd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 13,
+ "commandName" : "[ksoftirqd/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 106,
+ "commandName" : "[kstrp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 83,
+ "commandName" : "[kswapd0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 2,
+ "commandName" : "[kthreadd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 86,
+ "commandName" : "[kthrotld]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 32626,
+ "commandName" : "[kworker/0:0-cgroup_destroy]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 16:50",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 7,
+ "commandName" : "[kworker/0:0H-events_highpri]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 81,
+ "commandName" : "[kworker/0:1H-kblockd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 47519,
+ "commandName" : "[kworker/0:2-events]",
+ "cpuUsage" : 0.1,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:34",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 47059,
+ "commandName" : "[kworker/u2:0-writeback]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:32",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 48342,
+ "commandName" : "[kworker/u2:1-ext4-rsv-conversion]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:40",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 48785,
+ "commandName" : "[kworker/u2:2-flush-8:0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 110,
+ "commandName" : "[kworker/u3:0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5970,
+ "commandName" : "[l2arc_feed]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 76,
+ "commandName" : "[md]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 15,
+ "commandName" : "[migration/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 95,
+ "commandName" : "[mld]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 10,
+ "commandName" : "[mm_percpu_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 162,
+ "commandName" : "[mpt/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 161,
+ "commandName" : "[mpt_poll_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5,
+ "commandName" : "[netns]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 22,
+ "commandName" : "[oom_reaper]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 222,
+ "commandName" : "[raid5wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 3,
+ "commandName" : "[rcu_gp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 4,
+ "commandName" : "[rcu_par_gp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 14,
+ "commandName" : "[rcu_sched]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 11,
+ "commandName" : "[rcu_tasks_rude_]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 12,
+ "commandName" : "[rcu_tasks_trace]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 89,
+ "commandName" : "[scsi_eh_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 91,
+ "commandName" : "[scsi_eh_1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 189,
+ "commandName" : "[scsi_eh_2]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 90,
+ "commandName" : "[scsi_tmf_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 92,
+ "commandName" : "[scsi_tmf_1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 190,
+ "commandName" : "[scsi_tmf_2]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5960,
+ "commandName" : "[spl_delay_taskq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5961,
+ "commandName" : "[spl_dynamic_tas]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5962,
+ "commandName" : "[spl_kmem_cache]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5959,
+ "commandName" : "[spl_system_task]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 74,
+ "commandName" : "[tpm_dev_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 94,
+ "commandName" : "[vfio-irqfd-clea]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 79,
+ "commandName" : "[watchdogd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 23,
+ "commandName" : "[writeback]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5969,
+ "commandName" : "[z_vdev_file]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 109,
+ "commandName" : "[zswap-shrink]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5963,
+ "commandName" : "[zvol]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 49926,
+ "commandName" : "awk -v info=0 -v full_strings=0 -v summary_only=0 -v quiet=0 -v multihost=0 -v green=\\033[1;32m -v darkgreen=\\033[0;32m -v red=\\033[1;31m -v yellow=\\033[1;33m -v magenta=\\033[1;35m -v normal=\\033[0;39m\\033[0;49m -v white=\\033[0;02m -v cyan=\\033[1;36m -v dblue=\\033[0;34m -v dgreen=\\033[0;32m -v timing=0 -v has_fflush=OK -v full_compliance=1 -v partial_run=1 -v error_fail=0 -v noncompliant_fail=0 -f /opt/rudder/share/commands/../lib/reports.awk",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 11872.0
+ },
+ {
+ "pid" : 24930,
+ "commandName" : "bash",
+ "cpuUsage" : 0.0,
+ "memory" : 0.7,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 8024.0
+ },
+ {
+ "pid" : 5927,
+ "commandName" : "bpfilter_umh",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2772.0
+ },
+ {
+ "pid" : 49922,
+ "commandName" : "cat",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 6328.0
+ },
+ {
+ "pid" : 50027,
+ "commandName" : "fusioninventory-agent: running task Inventory",
+ "cpuUsage" : 47.0,
+ "memory" : 10.1,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 50496.0
+ },
+ {
+ "pid" : 5878,
+ "commandName" : "lxcfs /var/snap/lxd/common/var/lib/lxcfs -p /var/snap/lxd/common/lxcfs.pid",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 85652.0
+ },
+ {
+ "pid" : 50040,
+ "commandName" : "ps -A -o user,pid,pcpu,pmem,vsz,tty,etime,command",
+ "cpuUsage" : 0.0,
+ "memory" : 0.3,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 7060.0
+ },
+ {
+ "pid" : 797,
+ "commandName" : "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups",
+ "cpuUsage" : 0.0,
+ "memory" : 1.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 15416.0
+ },
+ {
+ "pid" : 24827,
+ "commandName" : "sshd: vagrant [priv]",
+ "cpuUsage" : 0.0,
+ "memory" : 1.3,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 17036.0
+ },
+ {
+ "pid" : 24914,
+ "commandName" : "sshd: vagrant@pts/0",
+ "cpuUsage" : 0.0,
+ "memory" : 1.3,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 17420.0
+ },
+ {
+ "pid" : 24929,
+ "commandName" : "su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.5,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 10592.0
+ },
+ {
+ "pid" : 24928,
+ "commandName" : "sudo su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 11892.0
+ },
+ {
+ "pid" : 24927,
+ "commandName" : "sudo su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.6,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/0",
+ "user" : "root",
+ "virtualMemory" : 11892.0
+ },
+ {
+ "pid" : 49924,
+ "commandName" : "tee /var/rudder/tmp/reports//2022-09-07T19:56:54+00:00@4d3a43bc-8508-46a2-92d7-cfe7320309a5.log",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 6192.0
+ }
+ ],
+ "processors" : [
+ {
+ "manufacturer" : "Intel",
+ "name" : "Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz",
+ "arch" : "i386",
+ "speed" : 2800,
+ "core" : 1,
+ "thread" : 1,
+ "stepping" : 9,
+ "family" : 6,
+ "model" : 158,
+ "quantity" : 1
+ }
+ ],
+ "slots" : [],
+ "software" : [
+ {
+ "name" : "libassuan0",
+ "version" : "2.5.5-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libassuan",
+ "sourceVersion" : "2.5.5-1build1"
+ },
+ {
+ "name" : "libbrotli1",
+ "version" : "1.0.9-2build6",
+ "publisher" : "Ubuntu",
+ "sourceName" : "brotli",
+ "sourceVersion" : "1.0.9-2build6"
+ },
+ {
+ "name" : "pastebinit",
+ "version" : "1.5.1-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.5.1-1ubuntu1"
+ },
+ {
+ "name" : "cryptsetup",
+ "version" : "2:2.4.3-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2:2.4.3-1ubuntu1"
+ },
+ {
+ "name" : "libstemmer0d",
+ "version" : "2.2.0-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "snowball",
+ "sourceVersion" : "2.2.0-1build1"
+ },
+ {
+ "name" : "python3-zope.interface",
+ "version" : "5.4.0-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "zope.interface",
+ "sourceVersion" : "5.4.0-1build1"
+ },
+ {
+ "name" : "libedit2",
+ "version" : "3.1-20210910-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libedit",
+ "sourceVersion" : "3.1-20210910-1build1"
+ },
+ {
+ "name" : "tcl8.6",
+ "version" : "8.6.12+dfsg-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "8.6.12+dfsg-1build1"
+ },
+ {
+ "name" : "gnupg-utils",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libcom-err2",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "e2fsprogs",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "libopeniscsiusr",
+ "version" : "2.1.5-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "open-iscsi",
+ "sourceVersion" : "2.1.5-1ubuntu1"
+ },
+ {
+ "name" : "adduser",
+ "version" : "3.118ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.118ubuntu5"
+ },
+ {
+ "name" : "libsasl2-2",
+ "version" : "2.1.27+dfsg2-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cyrus-sasl2",
+ "sourceVersion" : "2.1.27+dfsg2-3ubuntu1"
+ },
+ {
+ "name" : "libglib2.0-data",
+ "version" : "2.72.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glib2.0",
+ "sourceVersion" : "2.72.1-1"
+ },
+ {
+ "name" : "modemmanager",
+ "version" : "1.18.6-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.18.6-1"
+ },
+ {
+ "name" : "python3-more-itertools",
+ "version" : "8.10.0-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "more-itertools",
+ "sourceVersion" : "8.10.0-2"
+ },
+ {
+ "name" : "bc",
+ "version" : "1.7.1-3build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.7.1-3build1"
+ },
+ {
+ "name" : "ed",
+ "version" : "1.18-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.18-1"
+ },
+ {
+ "name" : "augeas-lenses",
+ "version" : "1.13.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "augeas",
+ "sourceVersion" : "1.13.0-1"
+ },
+ {
+ "name" : "libplymouth5",
+ "version" : "0.9.5+git20211018-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "plymouth",
+ "sourceVersion" : "0.9.5+git20211018-1ubuntu3"
+ },
+ {
+ "name" : "fdisk",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "python3-certifi",
+ "version" : "2020.6.20-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-certifi",
+ "sourceVersion" : "2020.6.20-1"
+ },
+ {
+ "name" : "multipath-tools",
+ "version" : "0.8.8-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.8.8-1ubuntu1"
+ },
+ {
+ "name" : "python3-gi",
+ "version" : "3.42.0-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pygobject",
+ "sourceVersion" : "3.42.0-3build1"
+ },
+ {
+ "name" : "keyboard-configuration",
+ "version" : "1.205ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "console-setup",
+ "sourceVersion" : "1.205ubuntu3"
+ },
+ {
+ "name" : "liburcu8",
+ "version" : "0.13.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "liburcu",
+ "sourceVersion" : "0.13.1-1"
+ },
+ {
+ "name" : "libklibc",
+ "version" : "2.0.10-4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "klibc",
+ "sourceVersion" : "2.0.10-4"
+ },
+ {
+ "name" : "python3-json-pointer",
+ "version" : "2.0-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-json-pointer",
+ "sourceVersion" : "2.0-0ubuntu1"
+ },
+ {
+ "name" : "grub-gfxpayload-lists",
+ "version" : "0.7",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.7"
+ },
+ {
+ "name" : "python3-requests",
+ "version" : "2.25.1+dfsg-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "requests",
+ "sourceVersion" : "2.25.1+dfsg-2"
+ },
+ {
+ "name" : "libdrm-common",
+ "version" : "2.4.110-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libdrm",
+ "sourceVersion" : "2.4.110-1ubuntu1"
+ },
+ {
+ "name" : "libp11-kit0",
+ "version" : "0.24.0-6build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "p11-kit",
+ "sourceVersion" : "0.24.0-6build1"
+ },
+ {
+ "name" : "install-info",
+ "version" : "6.8-4build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "texinfo",
+ "sourceVersion" : "6.8-4build1"
+ },
+ {
+ "name" : "liblmdb0",
+ "version" : "0.9.24-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lmdb",
+ "sourceVersion" : "0.9.24-1build2"
+ },
+ {
+ "name" : "linux-image-virtual",
+ "version" : "5.15.0.41.43",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-meta",
+ "sourceVersion" : "5.15.0.41.43"
+ },
+ {
+ "name" : "python3-pyrsistent",
+ "version" : "0.18.1-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyrsistent",
+ "sourceVersion" : "0.18.1-1build1"
+ },
+ {
+ "name" : "snapd",
+ "version" : "2.55.5+22.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.55.5+22.4"
+ },
+ {
+ "name" : "libgudev-1.0-0",
+ "version" : "1:237-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libgudev (237-2build1)",
+ "sourceVersion" : "237-2build1"
+ },
+ {
+ "name" : "grep",
+ "version" : "3.7-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.7-1build1"
+ },
+ {
+ "name" : "fuse3",
+ "version" : "3.10.5-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.10.5-1build1"
+ },
+ {
+ "name" : "libcrypt1",
+ "version" : "1:4.4.27-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxcrypt",
+ "sourceVersion" : "1:4.4.27-1"
+ },
+ {
+ "name" : "libglib2.0-bin",
+ "version" : "2.72.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glib2.0",
+ "sourceVersion" : "2.72.1-1"
+ },
+ {
+ "name" : "libmd0",
+ "version" : "1.0.4-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmd",
+ "sourceVersion" : "1.0.4-1build1"
+ },
+ {
+ "name" : "libnspr4",
+ "version" : "2:4.32-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nspr",
+ "sourceVersion" : "2:4.32-3build1"
+ },
+ {
+ "name" : "libapparmor1",
+ "version" : "3.0.4-2ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apparmor",
+ "sourceVersion" : "3.0.4-2ubuntu2.1"
+ },
+ {
+ "name" : "gzip",
+ "version" : "1.10-4ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.10-4ubuntu4"
+ },
+ {
+ "name" : "libintl-xs-perl",
+ "version" : "1.26-3build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libintl-perl",
+ "sourceVersion" : "1.26-3build2"
+ },
+ {
+ "name" : "libqmi-proxy",
+ "version" : "1.30.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libqmi",
+ "sourceVersion" : "1.30.4-1"
+ },
+ {
+ "name" : "python3-urllib3",
+ "version" : "1.26.5-1~exp1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-urllib3",
+ "sourceVersion" : "1.26.5-1~exp1"
+ },
+ {
+ "name" : "libefiboot1",
+ "version" : "37-6ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "efivar",
+ "sourceVersion" : "37-6ubuntu2"
+ },
+ {
+ "name" : "python3-apt",
+ "version" : "2.3.0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-apt",
+ "sourceVersion" : "2.3.0ubuntu2"
+ },
+ {
+ "name" : "libffi8",
+ "version" : "3.4.2-4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libffi",
+ "sourceVersion" : "3.4.2-4"
+ },
+ {
+ "name" : "pciutils",
+ "version" : "1:3.7.0-6",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:3.7.0-6"
+ },
+ {
+ "name" : "lsb-release",
+ "version" : "11.1.0ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lsb",
+ "sourceVersion" : "11.1.0ubuntu4"
+ },
+ {
+ "name" : "debconf",
+ "version" : "1.5.79ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.5.79ubuntu1"
+ },
+ {
+ "name" : "libfastjson4",
+ "version" : "0.99.9-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libfastjson",
+ "sourceVersion" : "0.99.9-1build2"
+ },
+ {
+ "name" : "libargon2-1",
+ "version" : "0~20171227-0.3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "argon2",
+ "sourceVersion" : "0~20171227-0.3"
+ },
+ {
+ "name" : "lsb-base",
+ "version" : "11.1.0ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lsb",
+ "sourceVersion" : "11.1.0ubuntu4"
+ },
+ {
+ "name" : "libkeyutils1",
+ "version" : "1.6.1-2ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "keyutils",
+ "sourceVersion" : "1.6.1-2ubuntu3"
+ },
+ {
+ "name" : "libnl-genl-3-200",
+ "version" : "3.5.0-0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnl3",
+ "sourceVersion" : "3.5.0-0.1"
+ },
+ {
+ "name" : "gpgsm",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "binutils",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "systemd-timesyncd",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "pinentry-curses",
+ "version" : "1.1.1-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pinentry",
+ "sourceVersion" : "1.1.1-1build2"
+ },
+ {
+ "name" : "python3-ptyprocess",
+ "version" : "0.7.0-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ptyprocess",
+ "sourceVersion" : "0.7.0-3"
+ },
+ {
+ "name" : "apparmor",
+ "version" : "3.0.4-2ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.0.4-2ubuntu2.1"
+ },
+ {
+ "name" : "python3-zipp",
+ "version" : "1.0.0-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-zipp",
+ "sourceVersion" : "1.0.0-3"
+ },
+ {
+ "name" : "libflashrom1",
+ "version" : "1.2-5build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "flashrom",
+ "sourceVersion" : "1.2-5build1"
+ },
+ {
+ "name" : "libdrm2",
+ "version" : "2.4.110-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libdrm",
+ "sourceVersion" : "2.4.110-1ubuntu1"
+ },
+ {
+ "name" : "libcurl3-gnutls",
+ "version" : "7.81.0-1ubuntu1.3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "curl",
+ "sourceVersion" : "7.81.0-1ubuntu1.3"
+ },
+ {
+ "name" : "libcryptsetup12",
+ "version" : "2:2.4.3-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cryptsetup",
+ "sourceVersion" : "2:2.4.3-1ubuntu1"
+ },
+ {
+ "name" : "python3-wadllib",
+ "version" : "1.3.6-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-wadllib",
+ "sourceVersion" : "1.3.6-1"
+ },
+ {
+ "name" : "linux-base",
+ "version" : "4.5ubuntu9",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.5ubuntu9"
+ },
+ {
+ "name" : "linux-headers-5.15.0-41",
+ "version" : "5.15.0-41.44",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux",
+ "sourceVersion" : "5.15.0-41.44"
+ },
+ {
+ "name" : "libnsl2",
+ "version" : "1.3.0-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnsl",
+ "sourceVersion" : "1.3.0-2build2"
+ },
+ {
+ "name" : "python3-oauthlib",
+ "version" : "3.2.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-oauthlib",
+ "sourceVersion" : "3.2.0-1"
+ },
+ {
+ "name" : "libsasl2-modules",
+ "version" : "2.1.27+dfsg2-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cyrus-sasl2",
+ "sourceVersion" : "2.1.27+dfsg2-3ubuntu1"
+ },
+ {
+ "name" : "base-files",
+ "version" : "12ubuntu4.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "12ubuntu4.1"
+ },
+ {
+ "name" : "libtext-charwidth-perl",
+ "version" : "0.4-10build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.4-10build3"
+ },
+ {
+ "name" : "pollinate",
+ "version" : "4.33-3ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.33-3ubuntu2"
+ },
+ {
+ "name" : "landscape-common",
+ "version" : "19.12-0ubuntu13",
+ "publisher" : "Ubuntu",
+ "sourceName" : "landscape-client",
+ "sourceVersion" : "19.12-0ubuntu13"
+ },
+ {
+ "name" : "libisc-export1105",
+ "version" : "1:9.11.19+dfsg-2.1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9-libs",
+ "sourceVersion" : "1:9.11.19+dfsg-2.1ubuntu3"
+ },
+ {
+ "name" : "sudo",
+ "version" : "1.9.9-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.9.9-1ubuntu2"
+ },
+ {
+ "name" : "liblz4-1",
+ "version" : "1.9.3-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lz4",
+ "sourceVersion" : "1.9.3-2build2"
+ },
+ {
+ "name" : "libattr1",
+ "version" : "1:2.5.1-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "attr",
+ "sourceVersion" : "1:2.5.1-1build1"
+ },
+ {
+ "name" : "udisks2",
+ "version" : "2.9.4-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.9.4-1ubuntu2"
+ },
+ {
+ "name" : "ucf",
+ "version" : "3.43",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.43"
+ },
+ {
+ "name" : "libpackagekit-glib2-18",
+ "version" : "1.2.5-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "packagekit",
+ "sourceVersion" : "1.2.5-2ubuntu2"
+ },
+ {
+ "name" : "libxml2",
+ "version" : "2.9.13+dfsg-1ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.9.13+dfsg-1ubuntu0.1"
+ },
+ {
+ "name" : "tcl",
+ "version" : "8.6.11+1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tcltk-defaults",
+ "sourceVersion" : "8.6.11+1build2"
+ },
+ {
+ "name" : "dmidecode",
+ "version" : "3.3-3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.3-3"
+ },
+ {
+ "name" : "usrmerge",
+ "version" : "25ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "25ubuntu2"
+ },
+ {
+ "name" : "libtext-wrapi18n-perl",
+ "version" : "0.6-9",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.6-9"
+ },
+ {
+ "name" : "libelf1",
+ "version" : "0.186-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "elfutils",
+ "sourceVersion" : "0.186-1build1"
+ },
+ {
+ "name" : "dbus-user-session",
+ "version" : "1.12.20-2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "dbus",
+ "sourceVersion" : "1.12.20-2ubuntu4"
+ },
+ {
+ "name" : "lvm2",
+ "version" : "2.3.11-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "ldap-utils",
+ "version" : "2.5.13+dfsg-0ubuntu0.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openldap",
+ "sourceVersion" : "2.5.13+dfsg-0ubuntu0.22.4.1"
+ },
+ {
+ "name" : "python3-click",
+ "version" : "8.0.3-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-click",
+ "sourceVersion" : "8.0.3-1"
+ },
+ {
+ "name" : "python3-babel",
+ "version" : "2.8.0+dfsg.1-7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-babel",
+ "sourceVersion" : "2.8.0+dfsg.1-7"
+ },
+ {
+ "name" : "wget",
+ "version" : "1.21.2-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.21.2-2ubuntu1"
+ },
+ {
+ "name" : "libreadline8",
+ "version" : "8.1.2-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "readline",
+ "sourceVersion" : "8.1.2-1"
+ },
+ {
+ "name" : "libsigsegv2",
+ "version" : "2.13-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsigsegv",
+ "sourceVersion" : "2.13-1ubuntu3"
+ },
+ {
+ "name" : "libxau6",
+ "version" : "1:1.0.9-1build5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxau",
+ "sourceVersion" : "1:1.0.9-1build5"
+ },
+ {
+ "name" : "grub-common",
+ "version" : "2.6-2ubuntu7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "grub2",
+ "sourceVersion" : "2.6-2ubuntu7"
+ },
+ {
+ "name" : "console-setup",
+ "version" : "1.205ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.205ubuntu3"
+ },
+ {
+ "name" : "net-tools",
+ "version" : "1.60+git20181103.0eebece-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.60+git20181103.0eebece-1ubuntu5"
+ },
+ {
+ "name" : "parted",
+ "version" : "3.4-2build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.4-2build1"
+ },
+ {
+ "name" : "libqmi-glib5",
+ "version" : "1.30.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libqmi",
+ "sourceVersion" : "1.30.4-1"
+ },
+ {
+ "name" : "ubuntu-minimal",
+ "version" : "1.481",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-meta",
+ "sourceVersion" : "1.481"
+ },
+ {
+ "name" : "python3-jsonschema",
+ "version" : "3.2.0-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-jsonschema",
+ "sourceVersion" : "3.2.0-0ubuntu2"
+ },
+ {
+ "name" : "powermgmt-base",
+ "version" : "1.36",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.36"
+ },
+ {
+ "name" : "libblockdev-loop2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "init-system-helpers",
+ "version" : "1.62",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.62"
+ },
+ {
+ "name" : "libxmlsec1",
+ "version" : "1.2.33-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xmlsec1",
+ "sourceVersion" : "1.2.33-1build2"
+ },
+ {
+ "name" : "python3-secretstorage",
+ "version" : "3.3.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-secretstorage",
+ "sourceVersion" : "3.3.1-1"
+ },
+ {
+ "name" : "manpages",
+ "version" : "5.10-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.10-1ubuntu1"
+ },
+ {
+ "name" : "gpg-wks-client",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "packagekit",
+ "version" : "1.2.5-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.2.5-2ubuntu2"
+ },
+ {
+ "name" : "sg3-utils-udev",
+ "version" : "1.46-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "sg3-utils",
+ "sourceVersion" : "1.46-1build1"
+ },
+ {
+ "name" : "libssh-4",
+ "version" : "0.9.6-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libssh",
+ "sourceVersion" : "0.9.6-2build1"
+ },
+ {
+ "name" : "python3-chardet",
+ "version" : "4.0.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "chardet",
+ "sourceVersion" : "4.0.0-1"
+ },
+ {
+ "name" : "software-properties-common",
+ "version" : "0.99.22.2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "software-properties",
+ "sourceVersion" : "0.99.22.2"
+ },
+ {
+ "name" : "xauth",
+ "version" : "1:1.1-1build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:1.1-1build2"
+ },
+ {
+ "name" : "libtirpc-common",
+ "version" : "1.3.2-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libtirpc",
+ "sourceVersion" : "1.3.2-2build1"
+ },
+ {
+ "name" : "tpm-udev",
+ "version" : "0.6",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.6"
+ },
+ {
+ "name" : "libpsl5",
+ "version" : "0.21.0-1.2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libpsl",
+ "sourceVersion" : "0.21.0-1.2build2"
+ },
+ {
+ "name" : "libtss2-esys-3.0.2-0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "usb.ids",
+ "version" : "2022.4.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2022.4.2-1"
+ },
+ {
+ "name" : "libmbim-glib4",
+ "version" : "1.26.2-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmbim",
+ "sourceVersion" : "1.26.2-1build1"
+ },
+ {
+ "name" : "packagekit-tools",
+ "version" : "1.2.5-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "packagekit",
+ "sourceVersion" : "1.2.5-2ubuntu2"
+ },
+ {
+ "name" : "ubuntu-advantage-tools",
+ "version" : "27.9~22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "27.9~22.4.1"
+ },
+ {
+ "name" : "htop",
+ "version" : "3.0.5-7build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.0.5-7build2"
+ },
+ {
+ "name" : "pwgen",
+ "version" : "2.8-2build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.8-2build1"
+ },
+ {
+ "name" : "e2fsprogs",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "libgmp10",
+ "version" : "2:6.2.1+dfsg-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gmp",
+ "sourceVersion" : "2:6.2.1+dfsg-3ubuntu1"
+ },
+ {
+ "name" : "libicu70",
+ "version" : "70.1-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "icu",
+ "sourceVersion" : "70.1-2"
+ },
+ {
+ "name" : "python3-jinja2",
+ "version" : "3.0.3-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "jinja2",
+ "sourceVersion" : "3.0.3-1"
+ },
+ {
+ "name" : "systemd",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "bolt",
+ "version" : "0.9.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.9.2-1"
+ },
+ {
+ "name" : "iputils-ping",
+ "version" : "3:20211215-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iputils",
+ "sourceVersion" : "3:20211215-1"
+ },
+ {
+ "name" : "libudisks2-0",
+ "version" : "2.9.4-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "udisks2",
+ "sourceVersion" : "2.9.4-1ubuntu2"
+ },
+ {
+ "name" : "ubuntu-server",
+ "version" : "1.481",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-meta",
+ "sourceVersion" : "1.481"
+ },
+ {
+ "name" : "python-babel-localedata",
+ "version" : "2.8.0+dfsg.1-7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-babel",
+ "sourceVersion" : "2.8.0+dfsg.1-7"
+ },
+ {
+ "name" : "libsort-naturally-perl",
+ "version" : "1.3-2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.3-2"
+ },
+ {
+ "name" : "libxmlsec1-openssl",
+ "version" : "1.2.33-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xmlsec1",
+ "sourceVersion" : "1.2.33-1build2"
+ },
+ {
+ "name" : "python3.10-minimal",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3.10",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "psmisc",
+ "version" : "23.4-2build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "23.4-2build3"
+ },
+ {
+ "name" : "base-passwd",
+ "version" : "3.5.52build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.5.52build1"
+ },
+ {
+ "name" : "libaudit1",
+ "version" : "1:3.0.7-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "audit",
+ "sourceVersion" : "1:3.0.7-1build1"
+ },
+ {
+ "name" : "dbus",
+ "version" : "1.12.20-2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.12.20-2ubuntu4"
+ },
+ {
+ "name" : "libjq1",
+ "version" : "1.6-2.1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "jq",
+ "sourceVersion" : "1.6-2.1ubuntu3"
+ },
+ {
+ "name" : "squashfs-tools",
+ "version" : "1:4.5-3build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:4.5-3build1"
+ },
+ {
+ "name" : "libmagic-mgc",
+ "version" : "1:5.41-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "file",
+ "sourceVersion" : "1:5.41-3"
+ },
+ {
+ "name" : "libip6tc2",
+ "version" : "1.8.7-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iptables",
+ "sourceVersion" : "1.8.7-1ubuntu5"
+ },
+ {
+ "name" : "libctf-nobfd0",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "python3-yaml",
+ "version" : "5.4.1-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyyaml",
+ "sourceVersion" : "5.4.1-1ubuntu1"
+ },
+ {
+ "name" : "gpg-wks-server",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libnuma1",
+ "version" : "2.0.14-3ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "numactl",
+ "sourceVersion" : "2.0.14-3ubuntu2"
+ },
+ {
+ "name" : "zsh-common",
+ "version" : "5.8.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "zsh",
+ "sourceVersion" : "5.8.1-1"
+ },
+ {
+ "name" : "dmsetup",
+ "version" : "2:1.2.175-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2 (2.03.11-2.1ubuntu4)",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "binutils-common",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "libtss2-tcti-device0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "sg3-utils",
+ "version" : "1.46-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.46-1build1"
+ },
+ {
+ "name" : "libkmod2",
+ "version" : "29-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "kmod",
+ "sourceVersion" : "29-1ubuntu1"
+ },
+ {
+ "name" : "python3-serial",
+ "version" : "3.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyserial",
+ "sourceVersion" : "3.5-1"
+ },
+ {
+ "name" : "libwrap0",
+ "version" : "7.6.q-31build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tcp-wrappers",
+ "sourceVersion" : "7.6.q-31build2"
+ },
+ {
+ "name" : "open-iscsi",
+ "version" : "2.1.5-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.1.5-1ubuntu1"
+ },
+ {
+ "name" : "libpython3.10-stdlib",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3.10",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "python3-debconf",
+ "version" : "1.5.79ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "debconf",
+ "sourceVersion" : "1.5.79ubuntu1"
+ },
+ {
+ "name" : "bsdutils",
+ "version" : "1:2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux (2.37.2-4ubuntu3)",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "zerofree",
+ "version" : "1.1.1-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.1.1-1build3"
+ },
+ {
+ "name" : "eatmydata",
+ "version" : "130-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libeatmydata",
+ "sourceVersion" : "130-2build1"
+ },
+ {
+ "name" : "libjcat1",
+ "version" : "0.1.9-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libjcat",
+ "sourceVersion" : "0.1.9-1"
+ },
+ {
+ "name" : "linux-headers-virtual",
+ "version" : "5.15.0.41.43",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-meta",
+ "sourceVersion" : "5.15.0.41.43"
+ },
+ {
+ "name" : "python3-lazr.uri",
+ "version" : "1.0.6-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lazr.uri",
+ "sourceVersion" : "1.0.6-2"
+ },
+ {
+ "name" : "libldap-common",
+ "version" : "2.5.12+dfsg-0ubuntu0.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openldap",
+ "sourceVersion" : "2.5.12+dfsg-0ubuntu0.22.4.1"
+ },
+ {
+ "name" : "policykit-1",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "kbd",
+ "version" : "2.3.0-3ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.3.0-3ubuntu4"
+ },
+ {
+ "name" : "byobu",
+ "version" : "5.133-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.133-1"
+ },
+ {
+ "name" : "procps",
+ "version" : "2:3.3.17-6ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2:3.3.17-6ubuntu2"
+ },
+ {
+ "name" : "xdg-user-dirs",
+ "version" : "0.17-2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.17-2ubuntu4"
+ },
+ {
+ "name" : "libfuse3-3",
+ "version" : "3.10.5-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fuse3",
+ "sourceVersion" : "3.10.5-1build1"
+ },
+ {
+ "name" : "dmeventd",
+ "version" : "2:1.2.175-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2 (2.03.11-2.1ubuntu4)",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "libgpm2",
+ "version" : "1.20.7-10build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gpm",
+ "sourceVersion" : "1.20.7-10build1"
+ },
+ {
+ "name" : "libdbus-1-3",
+ "version" : "1.12.20-2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "dbus",
+ "sourceVersion" : "1.12.20-2ubuntu4"
+ },
+ {
+ "name" : "python3-hamcrest",
+ "version" : "2.0.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyhamcrest",
+ "sourceVersion" : "2.0.2-2"
+ },
+ {
+ "name" : "update-notifier-common",
+ "version" : "3.192.54",
+ "publisher" : "Ubuntu",
+ "sourceName" : "update-notifier",
+ "sourceVersion" : "3.192.54"
+ },
+ {
+ "name" : "libevent-core-2.1-7",
+ "version" : "2.1.12-stable-1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libevent",
+ "sourceVersion" : "2.1.12-stable-1build3"
+ },
+ {
+ "name" : "sysvinit-utils",
+ "version" : "3.1-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "sysvinit",
+ "sourceVersion" : "3.1-1ubuntu1"
+ },
+ {
+ "name" : "python3-lazr.restfulclient",
+ "version" : "0.14.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lazr.restfulclient",
+ "sourceVersion" : "0.14.4-1"
+ },
+ {
+ "name" : "debconf-i18n",
+ "version" : "1.5.79ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "debconf",
+ "sourceVersion" : "1.5.79ubuntu1"
+ },
+ {
+ "name" : "libxxhash0",
+ "version" : "0.8.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xxhash",
+ "sourceVersion" : "0.8.1-1"
+ },
+ {
+ "name" : "unattended-upgrades",
+ "version" : "2.8ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.8ubuntu1"
+ },
+ {
+ "name" : "libjson-glib-1.0-common",
+ "version" : "1.6.6-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "json-glib",
+ "sourceVersion" : "1.6.6-1build1"
+ },
+ {
+ "name" : "ncurses-term",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libpam-systemd",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "liblzo2-2",
+ "version" : "2.10-2build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lzo2",
+ "sourceVersion" : "2.10-2build3"
+ },
+ {
+ "name" : "libnetplan0",
+ "version" : "0.104-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "netplan.io",
+ "sourceVersion" : "0.104-0ubuntu2"
+ },
+ {
+ "name" : "cloud-initramfs-dyn-netconf",
+ "version" : "0.47ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cloud-initramfs-tools",
+ "sourceVersion" : "0.47ubuntu1"
+ },
+ {
+ "name" : "libuv1",
+ "version" : "1.43.0-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.43.0-1"
+ },
+ {
+ "name" : "ethtool",
+ "version" : "1:5.16-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:5.16-1"
+ },
+ {
+ "name" : "cron",
+ "version" : "3.0pl1-137ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.0pl1-137ubuntu3"
+ },
+ {
+ "name" : "libnftnl11",
+ "version" : "1.2.1-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnftnl",
+ "sourceVersion" : "1.2.1-1build1"
+ },
+ {
+ "name" : "libgcrypt20",
+ "version" : "1.9.4-3ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.9.4-3ubuntu3"
+ },
+ {
+ "name" : "libntfs-3g89",
+ "version" : "1:2021.8.22-3ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ntfs-3g",
+ "sourceVersion" : "1:2021.8.22-3ubuntu1.1"
+ },
+ {
+ "name" : "usb-modeswitch",
+ "version" : "2.6.1-3ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.6.1-3ubuntu2"
+ },
+ {
+ "name" : "distro-info",
+ "version" : "1.1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.1build1"
+ },
+ {
+ "name" : "libvolume-key1",
+ "version" : "0.3.12-3.1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "volume-key",
+ "sourceVersion" : "0.3.12-3.1build3"
+ },
+ {
+ "name" : "libksba8",
+ "version" : "1.6.0-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libksba",
+ "sourceVersion" : "1.6.0-2build1"
+ },
+ {
+ "name" : "libnetfilter-conntrack3",
+ "version" : "1.0.9-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnetfilter-conntrack",
+ "sourceVersion" : "1.0.9-1"
+ },
+ {
+ "name" : "shared-mime-info",
+ "version" : "2.1-2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.1-2"
+ },
+ {
+ "name" : "distro-info-data",
+ "version" : "0.52ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.52ubuntu0.1"
+ },
+ {
+ "name" : "libtss2-tcti-swtpm0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "libaudit-common",
+ "version" : "1:3.0.7-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "audit",
+ "sourceVersion" : "1:3.0.7-1build1"
+ },
+ {
+ "name" : "libtcl8.6",
+ "version" : "8.6.12+dfsg-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tcl8.6",
+ "sourceVersion" : "8.6.12+dfsg-1build1"
+ },
+ {
+ "name" : "overlayroot",
+ "version" : "0.47ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cloud-initramfs-tools",
+ "sourceVersion" : "0.47ubuntu1"
+ },
+ {
+ "name" : "python3-keyring",
+ "version" : "23.5.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-keyring",
+ "sourceVersion" : "23.5.0-1"
+ },
+ {
+ "name" : "irqbalance",
+ "version" : "1.8.0-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.8.0-1build1"
+ },
+ {
+ "name" : "xfsprogs",
+ "version" : "5.13.0-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.13.0-1ubuntu2"
+ },
+ {
+ "name" : "libunistring2",
+ "version" : "1.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libunistring",
+ "sourceVersion" : "1.0-1"
+ },
+ {
+ "name" : "libx11-data",
+ "version" : "2:1.7.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libx11",
+ "sourceVersion" : "2:1.7.5-1"
+ },
+ {
+ "name" : "libpam-cap",
+ "version" : "1:2.44-1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libcap2",
+ "sourceVersion" : "1:2.44-1build3"
+ },
+ {
+ "name" : "initramfs-tools",
+ "version" : "0.140ubuntu13",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.140ubuntu13"
+ },
+ {
+ "name" : "hdparm",
+ "version" : "9.60+ds-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "9.60+ds-1build3"
+ },
+ {
+ "name" : "isc-dhcp-common",
+ "version" : "4.4.1-2.3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "isc-dhcp",
+ "sourceVersion" : "4.4.1-2.3ubuntu2.1"
+ },
+ {
+ "name" : "libparted-fs-resize0",
+ "version" : "3.4-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "parted",
+ "sourceVersion" : "3.4-2build1"
+ },
+ {
+ "name" : "bind9-dnsutils",
+ "version" : "1:9.18.1-1ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9",
+ "sourceVersion" : "1:9.18.1-1ubuntu1.1"
+ },
+ {
+ "name" : "libxext6",
+ "version" : "2:1.3.4-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxext",
+ "sourceVersion" : "2:1.3.4-1build1"
+ },
+ {
+ "name" : "liblocale-gettext-perl",
+ "version" : "1.7-4build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.7-4build3"
+ },
+ {
+ "name" : "librtmp1",
+ "version" : "2.4+20151223.gitfa8646d.1-2build4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "rtmpdump",
+ "sourceVersion" : "2.4+20151223.gitfa8646d.1-2build4"
+ },
+ {
+ "name" : "secureboot-db",
+ "version" : "1.8",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.8"
+ },
+ {
+ "name" : "libcbor0.8",
+ "version" : "0.8.0-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libcbor",
+ "sourceVersion" : "0.8.0-2ubuntu1"
+ },
+ {
+ "name" : "python3-problem-report",
+ "version" : "2.20.11-0ubuntu82.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apport",
+ "sourceVersion" : "2.20.11-0ubuntu82.1"
+ },
+ {
+ "name" : "iptables",
+ "version" : "1.8.7-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.8.7-1ubuntu5"
+ },
+ {
+ "name" : "rsync",
+ "version" : "3.2.3-8ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.2.3-8ubuntu3"
+ },
+ {
+ "name" : "klibc-utils",
+ "version" : "2.0.10-4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "klibc",
+ "sourceVersion" : "2.0.10-4"
+ },
+ {
+ "name" : "nftables",
+ "version" : "1.0.2-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.0.2-1ubuntu2"
+ },
+ {
+ "name" : "zlib1g",
+ "version" : "1:1.2.11.dfsg-2ubuntu9",
+ "publisher" : "Ubuntu",
+ "sourceName" : "zlib",
+ "sourceVersion" : "1:1.2.11.dfsg-2ubuntu9"
+ },
+ {
+ "name" : "vim",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "libip4tc2",
+ "version" : "1.8.7-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iptables",
+ "sourceVersion" : "1.8.7-1ubuntu5"
+ },
+ {
+ "name" : "libonig5",
+ "version" : "6.9.7.1-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libonig",
+ "sourceVersion" : "6.9.7.1-2build1"
+ },
+ {
+ "name" : "grub2-common",
+ "version" : "2.6-2ubuntu7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "grub2",
+ "sourceVersion" : "2.6-2ubuntu7"
+ },
+ {
+ "name" : "command-not-found",
+ "version" : "22.4.0",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "22.4.0"
+ },
+ {
+ "name" : "python3-importlib-metadata",
+ "version" : "4.6.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-importlib-metadata",
+ "sourceVersion" : "4.6.4-1"
+ },
+ {
+ "name" : "liblzma5",
+ "version" : "5.2.5-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xz-utils",
+ "sourceVersion" : "5.2.5-2ubuntu1"
+ },
+ {
+ "name" : "libperl5.34",
+ "version" : "5.34.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "perl",
+ "sourceVersion" : "5.34.0-3ubuntu1"
+ },
+ {
+ "name" : "dirmngr",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libparted2",
+ "version" : "3.4-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "parted",
+ "sourceVersion" : "3.4-2build1"
+ },
+ {
+ "name" : "cryptsetup-initramfs",
+ "version" : "2:2.4.3-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cryptsetup",
+ "sourceVersion" : "2:2.4.3-1ubuntu1"
+ },
+ {
+ "name" : "tzdata",
+ "version" : "2022a-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2022a-0ubuntu1"
+ },
+ {
+ "name" : "ca-certificates",
+ "version" : "20211016",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "20211016"
+ },
+ {
+ "name" : "python3-configobj",
+ "version" : "5.0.6-5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "configobj",
+ "sourceVersion" : "5.0.6-5"
+ },
+ {
+ "name" : "libzstd1",
+ "version" : "1.4.8+dfsg-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libzstd",
+ "sourceVersion" : "1.4.8+dfsg-3build1"
+ },
+ {
+ "name" : "libblkid1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libctf0",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "coreutils",
+ "version" : "8.32-4.1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "8.32-4.1ubuntu1"
+ },
+ {
+ "name" : "xxd",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "vim",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "apt-utils",
+ "version" : "2.4.5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apt",
+ "sourceVersion" : "2.4.5"
+ },
+ {
+ "name" : "strace",
+ "version" : "5.16-0ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.16-0ubuntu3"
+ },
+ {
+ "name" : "libnpth0",
+ "version" : "1.6-3build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "npth",
+ "sourceVersion" : "1.6-3build2"
+ },
+ {
+ "name" : "libxmlb2",
+ "version" : "0.3.6-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxmlb",
+ "sourceVersion" : "0.3.6-2build1"
+ },
+ {
+ "name" : "python3-pyasn1",
+ "version" : "0.4.8-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyasn1",
+ "sourceVersion" : "0.4.8-1"
+ },
+ {
+ "name" : "libnl-3-200",
+ "version" : "3.5.0-0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnl3",
+ "sourceVersion" : "3.5.0-0.1"
+ },
+ {
+ "name" : "libtasn1-6",
+ "version" : "4.18.0-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.18.0-4build1"
+ },
+ {
+ "name" : "busybox-initramfs",
+ "version" : "1:1.30.1-7ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "busybox",
+ "sourceVersion" : "1:1.30.1-7ubuntu3"
+ },
+ {
+ "name" : "dpkg",
+ "version" : "1.21.1ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.21.1ubuntu2.1"
+ },
+ {
+ "name" : "ubuntu-standard",
+ "version" : "1.481",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-meta",
+ "sourceVersion" : "1.481"
+ },
+ {
+ "name" : "libgusb2",
+ "version" : "0.3.10-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libgusb",
+ "sourceVersion" : "0.3.10-1"
+ },
+ {
+ "name" : "libpython3-stdlib",
+ "version" : "3.10.4-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-defaults",
+ "sourceVersion" : "3.10.4-0ubuntu2"
+ },
+ {
+ "name" : "libblockdev-swap2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "cloud-init",
+ "version" : "22.2-0ubuntu1~22.4.3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "22.2-0ubuntu1~22.4.3"
+ },
+ {
+ "name" : "libexpat1",
+ "version" : "2.4.7-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "expat",
+ "sourceVersion" : "2.4.7-1"
+ },
+ {
+ "name" : "openssh-sftp-server",
+ "version" : "1:8.9p1-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openssh",
+ "sourceVersion" : "1:8.9p1-3"
+ },
+ {
+ "name" : "libpam-modules-bin",
+ "version" : "1.4.0-11ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pam",
+ "sourceVersion" : "1.4.0-11ubuntu2"
+ },
+ {
+ "name" : "linux-headers-5.15.0-41-generic",
+ "version" : "5.15.0-41.44",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux",
+ "sourceVersion" : "5.15.0-41.44"
+ },
+ {
+ "name" : "tcpdump",
+ "version" : "4.99.1-3build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.99.1-3build2"
+ },
+ {
+ "name" : "logsave",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "e2fsprogs",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "linux-virtual",
+ "version" : "5.15.0.41.43",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-meta",
+ "sourceVersion" : "5.15.0.41.43"
+ },
+ {
+ "name" : "python3-gdbm",
+ "version" : "3.10.4-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-stdlib-extensions",
+ "sourceVersion" : "3.10.4-0ubuntu1"
+ },
+ {
+ "name" : "python3-markupsafe",
+ "version" : "2.0.1-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "markupsafe",
+ "sourceVersion" : "2.0.1-2build1"
+ },
+ {
+ "name" : "libgssapi-krb5-2",
+ "version" : "1.19.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "krb5",
+ "sourceVersion" : "1.19.2-2"
+ },
+ {
+ "name" : "ssh-import-id",
+ "version" : "5.11-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.11-0ubuntu1"
+ },
+ {
+ "name" : "screen",
+ "version" : "4.9.0-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.9.0-1"
+ },
+ {
+ "name" : "logrotate",
+ "version" : "3.19.0-1ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.19.0-1ubuntu1.1"
+ },
+ {
+ "name" : "libfido2-1",
+ "version" : "1.10.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libfido2",
+ "sourceVersion" : "1.10.0-1"
+ },
+ {
+ "name" : "debianutils",
+ "version" : "5.5-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.5-1ubuntu2"
+ },
+ {
+ "name" : "perl-modules-5.34",
+ "version" : "5.34.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "perl",
+ "sourceVersion" : "5.34.0-3ubuntu1"
+ },
+ {
+ "name" : "libpam0g",
+ "version" : "1.4.0-11ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pam",
+ "sourceVersion" : "1.4.0-11ubuntu2"
+ },
+ {
+ "name" : "netbase",
+ "version" : "6.3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "6.3"
+ },
+ {
+ "name" : "python3-dbus",
+ "version" : "1.2.18-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "dbus-python",
+ "sourceVersion" : "1.2.18-3build1"
+ },
+ {
+ "name" : "augeas-tools",
+ "version" : "1.13.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "augeas",
+ "sourceVersion" : "1.13.0-1"
+ },
+ {
+ "name" : "pkexec",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceName" : "policykit-1",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "plymouth-theme-ubuntu-text",
+ "version" : "0.9.5+git20211018-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "plymouth",
+ "sourceVersion" : "0.9.5+git20211018-1ubuntu3"
+ },
+ {
+ "name" : "polkitd",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceName" : "policykit-1",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "libxcb1",
+ "version" : "1.14-3ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxcb",
+ "sourceVersion" : "1.14-3ubuntu3"
+ },
+ {
+ "name" : "libpam-runtime",
+ "version" : "1.4.0-11ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pam",
+ "sourceVersion" : "1.4.0-11ubuntu2"
+ },
+ {
+ "name" : "xz-utils",
+ "version" : "5.2.5-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.2.5-2ubuntu1"
+ },
+ {
+ "name" : "libgirepository-1.0-1",
+ "version" : "1.72.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gobject-introspection",
+ "sourceVersion" : "1.72.0-1"
+ },
+ {
+ "name" : "libselinux1",
+ "version" : "3.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libselinux",
+ "sourceVersion" : "3.3-1build2"
+ },
+ {
+ "name" : "cpio",
+ "version" : "2.13+dfsg-7",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.13+dfsg-7"
+ },
+ {
+ "name" : "libpython3.10",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3.10",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "libdevmapper-event1.02.1",
+ "version" : "2:1.2.175-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2 (2.03.11-2.1ubuntu4)",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "python3-pkg-resources",
+ "version" : "59.6.0-1.2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "setuptools",
+ "sourceVersion" : "59.6.0-1.2"
+ },
+ {
+ "name" : "mtr-tiny",
+ "version" : "0.95-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "mtr",
+ "sourceVersion" : "0.95-1"
+ },
+ {
+ "name" : "libterm-readkey-perl",
+ "version" : "2.38-1build4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.38-1build4"
+ },
+ {
+ "name" : "bcache-tools",
+ "version" : "1.0.8-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.0.8-4ubuntu3"
+ },
+ {
+ "name" : "initramfs-tools-bin",
+ "version" : "0.140ubuntu13",
+ "publisher" : "Ubuntu",
+ "sourceName" : "initramfs-tools",
+ "sourceVersion" : "0.140ubuntu13"
+ },
+ {
+ "name" : "libsgutils2-2",
+ "version" : "1.46-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "sg3-utils",
+ "sourceVersion" : "1.46-1build1"
+ },
+ {
+ "name" : "libmodule-find-perl",
+ "version" : "0.15-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.15-1"
+ },
+ {
+ "name" : "isc-dhcp-client",
+ "version" : "4.4.1-2.3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "isc-dhcp",
+ "sourceVersion" : "4.4.1-2.3ubuntu2.1"
+ },
+ {
+ "name" : "libaio1",
+ "version" : "0.3.112-13build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libaio",
+ "sourceVersion" : "0.3.112-13build1"
+ },
+ {
+ "name" : "libgnutls30",
+ "version" : "3.7.3-4ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnutls28",
+ "sourceVersion" : "3.7.3-4ubuntu1"
+ },
+ {
+ "name" : "libsmartcols1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "openssl",
+ "version" : "3.0.2-0ubuntu1.6",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.0.2-0ubuntu1.6"
+ },
+ {
+ "name" : "plymouth",
+ "version" : "0.9.5+git20211018-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.9.5+git20211018-1ubuntu3"
+ },
+ {
+ "name" : "libcap-ng0",
+ "version" : "0.7.9-2.2build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libcap-ng",
+ "sourceVersion" : "0.7.9-2.2build3"
+ },
+ {
+ "name" : "python3-debian",
+ "version" : "0.1.43ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-debian",
+ "sourceVersion" : "0.1.43ubuntu1"
+ },
+ {
+ "name" : "thin-provisioning-tools",
+ "version" : "0.9.0-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.9.0-2ubuntu1"
+ },
+ {
+ "name" : "apport-symptoms",
+ "version" : "0.24",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.24"
+ },
+ {
+ "name" : "liblvm2cmd2.03",
+ "version" : "2.3.11-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "libdw1",
+ "version" : "0.186-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "elfutils",
+ "sourceVersion" : "0.186-1build1"
+ },
+ {
+ "name" : "gir1.2-glib-2.0",
+ "version" : "1.72.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gobject-introspection",
+ "sourceVersion" : "1.72.0-1"
+ },
+ {
+ "name" : "libtss2-sys1",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "python3-distutils",
+ "version" : "3.10.4-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-stdlib-extensions",
+ "sourceVersion" : "3.10.4-0ubuntu1"
+ },
+ {
+ "name" : "curl",
+ "version" : "7.81.0-1ubuntu1.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "7.81.0-1ubuntu1.4"
+ },
+ {
+ "name" : "mount",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libnettle8",
+ "version" : "3.7.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nettle",
+ "sourceVersion" : "3.7.3-1build2"
+ },
+ {
+ "name" : "linux-headers-generic",
+ "version" : "5.15.0.41.43",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-meta",
+ "sourceVersion" : "5.15.0.41.43"
+ },
+ {
+ "name" : "libprocps8",
+ "version" : "2:3.3.17-6ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "procps",
+ "sourceVersion" : "2:3.3.17-6ubuntu2"
+ },
+ {
+ "name" : "libintl-perl",
+ "version" : "1.26-3build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.26-3build2"
+ },
+ {
+ "name" : "libgdbm6",
+ "version" : "1.23-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gdbm",
+ "sourceVersion" : "1.23-1"
+ },
+ {
+ "name" : "python3-setuptools",
+ "version" : "59.6.0-1.2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "setuptools",
+ "sourceVersion" : "59.6.0-1.2"
+ },
+ {
+ "name" : "tree",
+ "version" : "2.0.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.0.2-1"
+ },
+ {
+ "name" : "libnewt0.52",
+ "version" : "0.52.21-5ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "newt",
+ "sourceVersion" : "0.52.21-5ubuntu2"
+ },
+ {
+ "name" : "python3-colorama",
+ "version" : "0.4.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-colorama",
+ "sourceVersion" : "0.4.4-1"
+ },
+ {
+ "name" : "dash",
+ "version" : "0.5.11+git20210903+57cd650a4ed-3build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.5.11+git20210903+57cd650a4ed-3build1"
+ },
+ {
+ "name" : "libfwupdplugin5",
+ "version" : "1.7.5-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fwupd",
+ "sourceVersion" : "1.7.5-3"
+ },
+ {
+ "name" : "libfribidi0",
+ "version" : "1.0.8-2ubuntu3.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fribidi",
+ "sourceVersion" : "1.0.8-2ubuntu3.1"
+ },
+ {
+ "name" : "libxslt1.1",
+ "version" : "1.1.34-4build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxslt",
+ "sourceVersion" : "1.1.34-4build2"
+ },
+ {
+ "name" : "binutils-x86-64-linux-gnu",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "libltdl7",
+ "version" : "2.4.6-15build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libtool",
+ "sourceVersion" : "2.4.6-15build2"
+ },
+ {
+ "name" : "libsasl2-modules-db",
+ "version" : "2.1.27+dfsg2-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cyrus-sasl2",
+ "sourceVersion" : "2.1.27+dfsg2-3ubuntu1"
+ },
+ {
+ "name" : "locales",
+ "version" : "2.35-0ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glibc",
+ "sourceVersion" : "2.35-0ubuntu3"
+ },
+ {
+ "name" : "libaugeas0",
+ "version" : "1.13.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "augeas",
+ "sourceVersion" : "1.13.0-1"
+ },
+ {
+ "name" : "libmodule-scandeps-perl",
+ "version" : "1.31-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.31-1"
+ },
+ {
+ "name" : "sosreport",
+ "version" : "4.3-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.3-1ubuntu2"
+ },
+ {
+ "name" : "python-apt-common",
+ "version" : "2.3.0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-apt",
+ "sourceVersion" : "2.3.0ubuntu2"
+ },
+ {
+ "name" : "libslang2",
+ "version" : "2.3.2-5build4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "slang2",
+ "sourceVersion" : "2.3.2-5build4"
+ },
+ {
+ "name" : "zstd",
+ "version" : "1.4.8+dfsg-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libzstd",
+ "sourceVersion" : "1.4.8+dfsg-3build1"
+ },
+ {
+ "name" : "libdb5.3",
+ "version" : "5.3.28+dfsg1-0.8ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "db5.3",
+ "sourceVersion" : "5.3.28+dfsg1-0.8ubuntu3"
+ },
+ {
+ "name" : "cloud-guest-utils",
+ "version" : "0.32-22-g45fe84a5-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cloud-utils",
+ "sourceVersion" : "0.32-22-g45fe84a5-0ubuntu1"
+ },
+ {
+ "name" : "libatm1",
+ "version" : "1:2.5.1-4build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-atm",
+ "sourceVersion" : "1:2.5.1-4build2"
+ },
+ {
+ "name" : "libext2fs2",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "e2fsprogs",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "python3-distro",
+ "version" : "1.7.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-distro",
+ "sourceVersion" : "1.7.0-1"
+ },
+ {
+ "name" : "eject",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libacl1",
+ "version" : "2.3.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "acl",
+ "sourceVersion" : "2.3.1-1"
+ },
+ {
+ "name" : "libnftables1",
+ "version" : "1.0.2-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nftables",
+ "sourceVersion" : "1.0.2-1ubuntu2"
+ },
+ {
+ "name" : "apt",
+ "version" : "2.4.5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.4.5"
+ },
+ {
+ "name" : "apport",
+ "version" : "2.20.11-0ubuntu82.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.20.11-0ubuntu82.1"
+ },
+ {
+ "name" : "kmod",
+ "version" : "29-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "29-1ubuntu1"
+ },
+ {
+ "name" : "xkb-data",
+ "version" : "2.33-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xkeyboard-config",
+ "sourceVersion" : "2.33-1"
+ },
+ {
+ "name" : "python3-pyparsing",
+ "version" : "2.4.7-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyparsing",
+ "sourceVersion" : "2.4.7-1"
+ },
+ {
+ "name" : "needrestart",
+ "version" : "3.5-5ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.5-5ubuntu2.1"
+ },
+ {
+ "name" : "libcap2",
+ "version" : "1:2.44-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:2.44-1build3"
+ },
+ {
+ "name" : "perl-base",
+ "version" : "5.34.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "perl",
+ "sourceVersion" : "5.34.0-3ubuntu1"
+ },
+ {
+ "name" : "rudder-agent",
+ "version" : "7.2.0~rc1-ubuntu22.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "7.2.0~rc1-ubuntu22.4"
+ },
+ {
+ "name" : "libgcab-1.0-0",
+ "version" : "1.4-3build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gcab",
+ "sourceVersion" : "1.4-3build2"
+ },
+ {
+ "name" : "motd-news-config",
+ "version" : "12ubuntu4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "base-files",
+ "sourceVersion" : "12ubuntu4.1"
+ },
+ {
+ "name" : "libfdisk1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libxtables12",
+ "version" : "1.8.7-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iptables",
+ "sourceVersion" : "1.8.7-1ubuntu5"
+ },
+ {
+ "name" : "tmux",
+ "version" : "3.2a-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.2a-4build1"
+ },
+ {
+ "name" : "gpgconf",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "iproute2",
+ "version" : "5.15.0-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.15.0-1ubuntu2"
+ },
+ {
+ "name" : "gpg",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "python3-jeepney",
+ "version" : "0.7.1-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "jeepney",
+ "sourceVersion" : "0.7.1-3"
+ },
+ {
+ "name" : "libpcre2-8-0",
+ "version" : "10.39-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pcre2",
+ "sourceVersion" : "10.39-3build1"
+ },
+ {
+ "name" : "uuid-runtime",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "openssh-server",
+ "version" : "1:8.9p1-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openssh",
+ "sourceVersion" : "1:8.9p1-3"
+ },
+ {
+ "name" : "ftp",
+ "version" : "20210827-4build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tnftp",
+ "sourceVersion" : "20210827-4build1"
+ },
+ {
+ "name" : "libestr0",
+ "version" : "0.1.10-2.1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libestr",
+ "sourceVersion" : "0.1.10-2.1build3"
+ },
+ {
+ "name" : "python3-pexpect",
+ "version" : "4.8.0-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pexpect",
+ "sourceVersion" : "4.8.0-2ubuntu1"
+ },
+ {
+ "name" : "time",
+ "version" : "1.9-0.1build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.9-0.1build2"
+ },
+ {
+ "name" : "python3-netifaces",
+ "version" : "0.11.0-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "netifaces",
+ "sourceVersion" : "0.11.0-1build2"
+ },
+ {
+ "name" : "libblockdev2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "rsyslog",
+ "version" : "8.2112.0-2ubuntu2.2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "8.2112.0-2ubuntu2.2"
+ },
+ {
+ "name" : "libblockdev-crypto2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "libkrb5support0",
+ "version" : "1.19.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "krb5",
+ "sourceVersion" : "1.19.2-2"
+ },
+ {
+ "name" : "libxml-treepp-perl",
+ "version" : "0.43-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.43-1"
+ },
+ {
+ "name" : "libnfnetlink0",
+ "version" : "1.0.1-3build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnfnetlink",
+ "sourceVersion" : "1.0.1-3build3"
+ },
+ {
+ "name" : "iputils-tracepath",
+ "version" : "3:20211215-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iputils",
+ "sourceVersion" : "3:20211215-1"
+ },
+ {
+ "name" : "bsdextrautils",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libnss3",
+ "version" : "2:3.68.2-0ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nss",
+ "sourceVersion" : "2:3.68.2-0ubuntu1.1"
+ },
+ {
+ "name" : "libglib2.0-0",
+ "version" : "2.72.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glib2.0",
+ "sourceVersion" : "2.72.1-1"
+ },
+ {
+ "name" : "libgpg-error0",
+ "version" : "1.43-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libgpg-error",
+ "sourceVersion" : "1.43-3"
+ },
+ {
+ "name" : "python3-newt",
+ "version" : "0.52.21-5ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "newt",
+ "sourceVersion" : "0.52.21-5ubuntu2"
+ },
+ {
+ "name" : "libpython3.10-minimal",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3.10",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "less",
+ "version" : "590-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "590-1build1"
+ },
+ {
+ "name" : "gawk",
+ "version" : "1:5.1.0-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:5.1.0-1build3"
+ },
+ {
+ "name" : "libc6",
+ "version" : "2.35-0ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glibc",
+ "sourceVersion" : "2.35-0ubuntu3"
+ },
+ {
+ "name" : "apt-transport-https",
+ "version" : "2.4.7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apt",
+ "sourceVersion" : "2.4.7"
+ },
+ {
+ "name" : "liberror-perl",
+ "version" : "0.17029-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.17029-1"
+ },
+ {
+ "name" : "libseccomp2",
+ "version" : "2.5.3-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libseccomp",
+ "sourceVersion" : "2.5.3-2ubuntu2"
+ },
+ {
+ "name" : "libmount1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "netcat-openbsd",
+ "version" : "1.218-4ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.218-4ubuntu1"
+ },
+ {
+ "name" : "systemd-sysv",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "libblockdev-utils2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "git",
+ "version" : "1:2.34.1-1ubuntu1.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:2.34.1-1ubuntu1.4"
+ },
+ {
+ "name" : "busybox-static",
+ "version" : "1:1.30.1-7ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "busybox",
+ "sourceVersion" : "1:1.30.1-7ubuntu3"
+ },
+ {
+ "name" : "usb-modeswitch-data",
+ "version" : "20191128-4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "20191128-4"
+ },
+ {
+ "name" : "libssl3",
+ "version" : "3.0.2-0ubuntu1.6",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openssl",
+ "sourceVersion" : "3.0.2-0ubuntu1.6"
+ },
+ {
+ "name" : "libblockdev-fs2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "libfwupd2",
+ "version" : "1.7.5-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fwupd",
+ "sourceVersion" : "1.7.5-3"
+ },
+ {
+ "name" : "file",
+ "version" : "1:5.41-3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:5.41-3"
+ },
+ {
+ "name" : "libuchardet0",
+ "version" : "0.0.7-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "uchardet",
+ "sourceVersion" : "0.0.7-1build2"
+ },
+ {
+ "name" : "info",
+ "version" : "6.8-4build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "texinfo",
+ "sourceVersion" : "6.8-4build1"
+ },
+ {
+ "name" : "vim-tiny",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "vim",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "gcc-12-base",
+ "version" : "12-20220319-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gcc-12",
+ "sourceVersion" : "12-20220319-1ubuntu1"
+ },
+ {
+ "name" : "libxmuu1",
+ "version" : "2:1.1.3-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxmu",
+ "sourceVersion" : "2:1.1.3-3"
+ },
+ {
+ "name" : "python3-distupgrade",
+ "version" : "1:22.4.11",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-release-upgrader",
+ "sourceVersion" : "1:22.4.11"
+ },
+ {
+ "name" : "python3-commandnotfound",
+ "version" : "22.4.0",
+ "publisher" : "Ubuntu",
+ "sourceName" : "command-not-found",
+ "sourceVersion" : "22.4.0"
+ },
+ {
+ "name" : "grub-pc-bin",
+ "version" : "2.6-2ubuntu7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "grub2",
+ "sourceVersion" : "2.6-2ubuntu7"
+ },
+ {
+ "name" : "python3-minimal",
+ "version" : "3.10.4-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-defaults",
+ "sourceVersion" : "3.10.4-0ubuntu2"
+ },
+ {
+ "name" : "patch",
+ "version" : "2.7.6-7build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.7.6-7build2"
+ },
+ {
+ "name" : "libsepol2",
+ "version" : "3.3-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsepol",
+ "sourceVersion" : "3.3-1build1"
+ },
+ {
+ "name" : "libinih1",
+ "version" : "53-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libinih",
+ "sourceVersion" : "53-1ubuntu3"
+ },
+ {
+ "name" : "udev",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "init",
+ "version" : "1.62",
+ "publisher" : "Ubuntu",
+ "sourceName" : "init-system-helpers",
+ "sourceVersion" : "1.62"
+ },
+ {
+ "name" : "libtinfo6",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libefivar1",
+ "version" : "37-6ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "efivar",
+ "sourceVersion" : "37-6ubuntu2"
+ },
+ {
+ "name" : "libpam-modules",
+ "version" : "1.4.0-11ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pam",
+ "sourceVersion" : "1.4.0-11ubuntu2"
+ },
+ {
+ "name" : "libjson-c5",
+ "version" : "0.15-3~ubuntu1.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "json-c",
+ "sourceVersion" : "0.15-3~ubuntu1.22.4.1"
+ },
+ {
+ "name" : "libsemanage-common",
+ "version" : "3.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsemanage",
+ "sourceVersion" : "3.3-1build2"
+ },
+ {
+ "name" : "libhogweed6",
+ "version" : "3.7.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nettle",
+ "sourceVersion" : "3.7.3-1build2"
+ },
+ {
+ "name" : "libjson-glib-1.0-0",
+ "version" : "1.6.6-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "json-glib",
+ "sourceVersion" : "1.6.6-1build1"
+ },
+ {
+ "name" : "libbz2-1.0",
+ "version" : "1.0.8-5build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bzip2",
+ "sourceVersion" : "1.0.8-5build1"
+ },
+ {
+ "name" : "ldapscripts",
+ "version" : "2.0.8-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.0.8-1ubuntu2"
+ },
+ {
+ "name" : "linux-image-5.15.0-41-generic",
+ "version" : "5.15.0-41.44",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-signed",
+ "sourceVersion" : "5.15.0-41.44"
+ },
+ {
+ "name" : "libkrb5-3",
+ "version" : "1.19.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "krb5",
+ "sourceVersion" : "1.19.2-2"
+ },
+ {
+ "name" : "libpcap0.8",
+ "version" : "1.10.1-4build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libpcap",
+ "sourceVersion" : "1.10.1-4build1"
+ },
+ {
+ "name" : "grub-pc",
+ "version" : "2.6-2ubuntu7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "grub2",
+ "sourceVersion" : "2.6-2ubuntu7"
+ },
+ {
+ "name" : "mawk",
+ "version" : "1.3.4.20200120-3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.3.4.20200120-3"
+ },
+ {
+ "name" : "libbpf0",
+ "version" : "1:0.5.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libbpf (0.5.0-1)",
+ "sourceVersion" : "0.5.0-1"
+ },
+ {
+ "name" : "libsqlite3-0",
+ "version" : "3.37.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "sqlite3",
+ "sourceVersion" : "3.37.2-2"
+ },
+ {
+ "name" : "libgcc-s1",
+ "version" : "12-20220319-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gcc-12",
+ "sourceVersion" : "12-20220319-1ubuntu1"
+ },
+ {
+ "name" : "libftdi1-2",
+ "version" : "1.5-5build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libftdi1",
+ "sourceVersion" : "1.5-5build3"
+ },
+ {
+ "name" : "libmbim-proxy",
+ "version" : "1.26.2-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmbim",
+ "sourceVersion" : "1.26.2-1build1"
+ },
+ {
+ "name" : "python3-update-manager",
+ "version" : "1:22.4.9",
+ "publisher" : "Ubuntu",
+ "sourceName" : "update-manager",
+ "sourceVersion" : "1:22.4.9"
+ },
+ {
+ "name" : "python3-idna",
+ "version" : "3.3-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-idna",
+ "sourceVersion" : "3.3-1"
+ },
+ {
+ "name" : "media-types",
+ "version" : "7.0.0",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "7.0.0"
+ },
+ {
+ "name" : "libpopt0",
+ "version" : "1.18-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "popt",
+ "sourceVersion" : "1.18-3build1"
+ },
+ {
+ "name" : "ubuntu-keyring",
+ "version" : "2021.3.26",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2021.3.26"
+ },
+ {
+ "name" : "libgpgme11",
+ "version" : "1.16.0-1.2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gpgme1.0",
+ "sourceVersion" : "1.16.0-1.2ubuntu4"
+ },
+ {
+ "name" : "ufw",
+ "version" : "0.36.1-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.36.1-4build1"
+ },
+ {
+ "name" : "python3-openssl",
+ "version" : "21.0.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyopenssl",
+ "sourceVersion" : "21.0.0-1"
+ },
+ {
+ "name" : "python3-bcrypt",
+ "version" : "3.2.0-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-bcrypt",
+ "sourceVersion" : "3.2.0-1build1"
+ },
+ {
+ "name" : "vim-runtime",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "vim",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "libpipeline1",
+ "version" : "1.5.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libpipeline",
+ "sourceVersion" : "1.5.5-1"
+ },
+ {
+ "name" : "libfreetype6",
+ "version" : "2.11.1+dfsg-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "freetype",
+ "sourceVersion" : "2.11.1+dfsg-1build1"
+ },
+ {
+ "name" : "python3-automat",
+ "version" : "20.2.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "automat",
+ "sourceVersion" : "20.2.0-1"
+ },
+ {
+ "name" : "finalrd",
+ "version" : "9build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "9build1"
+ },
+ {
+ "name" : "jq",
+ "version" : "1.6-2.1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.6-2.1ubuntu3"
+ },
+ {
+ "name" : "libldap-2.5-0",
+ "version" : "2.5.13+dfsg-0ubuntu0.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openldap",
+ "sourceVersion" : "2.5.13+dfsg-0ubuntu0.22.4.1"
+ },
+ {
+ "name" : "cryptsetup-bin",
+ "version" : "2:2.4.3-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cryptsetup",
+ "sourceVersion" : "2:2.4.3-1ubuntu1"
+ },
+ {
+ "name" : "sed",
+ "version" : "4.8-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.8-1ubuntu2"
+ },
+ {
+ "name" : "libmnl0",
+ "version" : "1.0.4-3build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmnl",
+ "sourceVersion" : "1.0.4-3build2"
+ },
+ {
+ "name" : "libisns0",
+ "version" : "0.101-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "open-isns",
+ "sourceVersion" : "0.101-0ubuntu2"
+ },
+ {
+ "name" : "libmaxminddb0",
+ "version" : "1.5.2-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmaxminddb",
+ "sourceVersion" : "1.5.2-1build2"
+ },
+ {
+ "name" : "libapt-pkg6.0",
+ "version" : "2.4.5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apt",
+ "sourceVersion" : "2.4.5"
+ },
+ {
+ "name" : "fwupd-signed",
+ "version" : "1.44+1.2-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fwupd-signed (1.44)",
+ "sourceVersion" : "1.44"
+ },
+ {
+ "name" : "hostname",
+ "version" : "3.23ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.23ubuntu2"
+ },
+ {
+ "name" : "libsodium23",
+ "version" : "1.0.18-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsodium",
+ "sourceVersion" : "1.0.18-1build2"
+ },
+ {
+ "name" : "gpgv",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "python3.10",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "man-db",
+ "version" : "2.10.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.10.2-1"
+ },
+ {
+ "name" : "libmm-glib0",
+ "version" : "1.18.6-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "modemmanager",
+ "sourceVersion" : "1.18.6-1"
+ },
+ {
+ "name" : "update-manager-core",
+ "version" : "1:22.4.9",
+ "publisher" : "Ubuntu",
+ "sourceName" : "update-manager",
+ "sourceVersion" : "1:22.4.9"
+ },
+ {
+ "name" : "python3-launchpadlib",
+ "version" : "1.10.16-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-launchpadlib",
+ "sourceVersion" : "1.10.16-1"
+ },
+ {
+ "name" : "fwupd",
+ "version" : "1.7.5-3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.7.5-3"
+ },
+ {
+ "name" : "sharutils",
+ "version" : "1:4.15.2-5build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:4.15.2-5build1"
+ },
+ {
+ "name" : "python3-jwt",
+ "version" : "2.3.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyjwt",
+ "sourceVersion" : "2.3.0-1"
+ },
+ {
+ "name" : "libproc-processtable-perl",
+ "version" : "0.634-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.634-1build1"
+ },
+ {
+ "name" : "python3-twisted",
+ "version" : "22.1.0-2ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "twisted",
+ "sourceVersion" : "22.1.0-2ubuntu2.1"
+ },
+ {
+ "name" : "libpcre3",
+ "version" : "2:8.39-13ubuntu0.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pcre3",
+ "sourceVersion" : "2:8.39-13ubuntu0.22.4.1"
+ },
+ {
+ "name" : "libmpdec3",
+ "version" : "2.5.1-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "mpdecimal",
+ "sourceVersion" : "2.5.1-2build2"
+ },
+ {
+ "name" : "libgstreamer1.0-0",
+ "version" : "1.20.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gstreamer1.0",
+ "sourceVersion" : "1.20.1-1"
+ },
+ {
+ "name" : "linux-modules-5.15.0-41-generic",
+ "version" : "5.15.0-41.44",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux",
+ "sourceVersion" : "5.15.0-41.44"
+ },
+ {
+ "name" : "libdns-export1110",
+ "version" : "1:9.11.19+dfsg-2.1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9-libs",
+ "sourceVersion" : "1:9.11.19+dfsg-2.1ubuntu3"
+ },
+ {
+ "name" : "fonts-ubuntu-console",
+ "version" : "0.83-6ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fonts-ubuntu",
+ "sourceVersion" : "0.83-6ubuntu1"
+ },
+ {
+ "name" : "gpg-agent",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libpolkit-agent-1-0",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceName" : "policykit-1",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "libmspack0",
+ "version" : "0.10.1-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmspack",
+ "sourceVersion" : "0.10.1-2build2"
+ },
+ {
+ "name" : "cloud-initramfs-copymods",
+ "version" : "0.47ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cloud-initramfs-tools",
+ "sourceVersion" : "0.47ubuntu1"
+ },
+ {
+ "name" : "python3-hyperlink",
+ "version" : "21.0.0-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "hyperlink",
+ "sourceVersion" : "21.0.0-3"
+ },
+ {
+ "name" : "iso-codes",
+ "version" : "4.9.0-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.9.0-1"
+ },
+ {
+ "name" : "kpartx",
+ "version" : "0.8.8-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "multipath-tools",
+ "sourceVersion" : "0.8.8-1ubuntu1"
+ },
+ {
+ "name" : "tar",
+ "version" : "1.34+dfsg-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.34+dfsg-1build3"
+ },
+ {
+ "name" : "openssh-client",
+ "version" : "1:8.9p1-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openssh",
+ "sourceVersion" : "1:8.9p1-3"
+ },
+ {
+ "name" : "pci.ids",
+ "version" : "0.0~2022.1.22-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.0~2022.1.22-1"
+ },
+ {
+ "name" : "python3-lib2to3",
+ "version" : "3.10.4-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-stdlib-extensions",
+ "sourceVersion" : "3.10.4-0ubuntu1"
+ },
+ {
+ "name" : "libtirpc3",
+ "version" : "1.3.2-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libtirpc",
+ "sourceVersion" : "1.3.2-2build1"
+ },
+ {
+ "name" : "python3-software-properties",
+ "version" : "0.99.22.2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "software-properties",
+ "sourceVersion" : "0.99.22.2"
+ },
+ {
+ "name" : "libsemanage2",
+ "version" : "3.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsemanage",
+ "sourceVersion" : "3.3-1build2"
+ },
+ {
+ "name" : "python3-service-identity",
+ "version" : "18.1.0-6",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-service-identity",
+ "sourceVersion" : "18.1.0-6"
+ },
+ {
+ "name" : "sensible-utils",
+ "version" : "0.0.17",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.0.17"
+ },
+ {
+ "name" : "login",
+ "version" : "1:4.8.1-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "shadow",
+ "sourceVersion" : "1:4.8.1-2ubuntu2"
+ },
+ {
+ "name" : "gdisk",
+ "version" : "1.0.8-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.0.8-4build1"
+ },
+ {
+ "name" : "lsof",
+ "version" : "4.93.2+dfsg-1.1build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.93.2+dfsg-1.1build2"
+ },
+ {
+ "name" : "open-vm-tools",
+ "version" : "2:11.3.5-1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2:11.3.5-1ubuntu4"
+ },
+ {
+ "name" : "libjansson4",
+ "version" : "2.13.1-1.1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "jansson",
+ "sourceVersion" : "2.13.1-1.1build3"
+ },
+ {
+ "name" : "ubuntu-release-upgrader-core",
+ "version" : "1:22.4.11",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-release-upgrader",
+ "sourceVersion" : "1:22.4.11"
+ },
+ {
+ "name" : "passwd",
+ "version" : "1:4.8.1-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "shadow",
+ "sourceVersion" : "1:4.8.1-2ubuntu2"
+ },
+ {
+ "name" : "libnss-systemd",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "util-linux",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libarchive13",
+ "version" : "3.6.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libarchive",
+ "sourceVersion" : "3.6.0-1ubuntu1"
+ },
+ {
+ "name" : "gnupg",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "lshw",
+ "version" : "2.19.git.2021.6.19.996aaad9c7-2build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.19.git.2021.6.19.996aaad9c7-2build1"
+ },
+ {
+ "name" : "diffutils",
+ "version" : "1:3.8-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:3.8-0ubuntu2"
+ },
+ {
+ "name" : "libcurl4",
+ "version" : "7.81.0-1ubuntu1.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "curl",
+ "sourceVersion" : "7.81.0-1ubuntu1.4"
+ },
+ {
+ "name" : "publicsuffix",
+ "version" : "20211207.1025-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "20211207.1025-1"
+ },
+ {
+ "name" : "os-prober",
+ "version" : "1.79ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.79ubuntu2"
+ },
+ {
+ "name" : "libpng16-16",
+ "version" : "1.6.37-3build5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libpng1.6",
+ "sourceVersion" : "1.6.37-3build5"
+ },
+ {
+ "name" : "friendly-recovery",
+ "version" : "0.2.42",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.2.42"
+ },
+ {
+ "name" : "telnet",
+ "version" : "0.17-44build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "netkit-telnet",
+ "sourceVersion" : "0.17-44build1"
+ },
+ {
+ "name" : "bash-completion",
+ "version" : "1:2.11-5ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:2.11-5ubuntu1"
+ },
+ {
+ "name" : "libncursesw6",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "python3-systemd",
+ "version" : "234-3ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-systemd",
+ "sourceVersion" : "234-3ubuntu2"
+ },
+ {
+ "name" : "groff-base",
+ "version" : "1.22.4-8build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "groff",
+ "sourceVersion" : "1.22.4-8build1"
+ },
+ {
+ "name" : "libx11-6",
+ "version" : "2:1.7.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libx11",
+ "sourceVersion" : "2:1.7.5-1"
+ },
+ {
+ "name" : "mdadm",
+ "version" : "4.2-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.2-0ubuntu1"
+ },
+ {
+ "name" : "bind9-host",
+ "version" : "1:9.18.1-1ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9",
+ "sourceVersion" : "1:9.18.1-1ubuntu1.1"
+ },
+ {
+ "name" : "libblockdev-part2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "bash",
+ "version" : "5.1-6ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.1-6ubuntu1"
+ },
+ {
+ "name" : "whiptail",
+ "version" : "0.52.21-5ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "newt",
+ "sourceVersion" : "0.52.21-5ubuntu2"
+ },
+ {
+ "name" : "libtss2-tcti-mssim0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "dosfstools",
+ "version" : "4.2-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.2-1build3"
+ },
+ {
+ "name" : "libudev1",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "python3",
+ "version" : "3.10.4-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-defaults",
+ "sourceVersion" : "3.10.4-0ubuntu2"
+ },
+ {
+ "name" : "perl",
+ "version" : "5.34.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.34.0-3ubuntu1"
+ },
+ {
+ "name" : "libappstream4",
+ "version" : "0.15.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "appstream",
+ "sourceVersion" : "0.15.2-2"
+ },
+ {
+ "name" : "libnghttp2-14",
+ "version" : "1.43.0-1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nghttp2",
+ "sourceVersion" : "1.43.0-1build3"
+ },
+ {
+ "name" : "networkd-dispatcher",
+ "version" : "2.1-2ubuntu0.22.4.2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.1-2ubuntu0.22.4.2"
+ },
+ {
+ "name" : "libusb-1.0-0",
+ "version" : "2:1.0.25-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libusb-1.0",
+ "sourceVersion" : "2:1.0.25-1ubuntu2"
+ },
+ {
+ "name" : "netplan.io",
+ "version" : "0.104-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.104-0ubuntu2"
+ },
+ {
+ "name" : "ntfs-3g",
+ "version" : "1:2021.8.22-3ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:2021.8.22-3ubuntu1.1"
+ },
+ {
+ "name" : "ncurses-base",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libpci3",
+ "version" : "1:3.7.0-6",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pciutils",
+ "sourceVersion" : "1:3.7.0-6"
+ },
+ {
+ "name" : "python3-jsonpatch",
+ "version" : "1.32-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-json-patch",
+ "sourceVersion" : "1.32-2"
+ },
+ {
+ "name" : "libtss2-tcti-cmd0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "libss2",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "e2fsprogs",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "readline-common",
+ "version" : "8.1.2-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "readline",
+ "sourceVersion" : "8.1.2-1"
+ },
+ {
+ "name" : "libncurses6",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libk5crypto3",
+ "version" : "1.19.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "krb5",
+ "sourceVersion" : "1.19.2-2"
+ },
+ {
+ "name" : "python3-cffi-backend",
+ "version" : "1.15.0-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-cffi",
+ "sourceVersion" : "1.15.0-1build2"
+ },
+ {
+ "name" : "python3-incremental",
+ "version" : "21.3.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "incremental",
+ "sourceVersion" : "21.3.0-1"
+ },
+ {
+ "name" : "libatasmart4",
+ "version" : "0.19-5build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libatasmart",
+ "sourceVersion" : "0.19-5build2"
+ },
+ {
+ "name" : "git-man",
+ "version" : "1:2.34.1-1ubuntu1.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "git",
+ "sourceVersion" : "1:2.34.1-1ubuntu1.4"
+ },
+ {
+ "name" : "libuuid1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libidn2-0",
+ "version" : "2.3.2-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libidn2",
+ "sourceVersion" : "2.3.2-2build1"
+ },
+ {
+ "name" : "python3-attr",
+ "version" : "21.2.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-attrs",
+ "sourceVersion" : "21.2.0-1"
+ },
+ {
+ "name" : "vim-common",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "vim",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "bind9-libs",
+ "version" : "1:9.18.1-1ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9",
+ "sourceVersion" : "1:9.18.1-1ubuntu1.1"
+ },
+ {
+ "name" : "findutils",
+ "version" : "4.8.0-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.8.0-1ubuntu3"
+ },
+ {
+ "name" : "sbsigntool",
+ "version" : "0.9.4-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.9.4-2ubuntu2"
+ },
+ {
+ "name" : "usbutils",
+ "version" : "1:14-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:14-1build1"
+ },
+ {
+ "name" : "libblockdev-part-err2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "libtext-iconv-perl",
+ "version" : "1.7-7build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.7-7build3"
+ },
+ {
+ "name" : "libsmbios-c2",
+ "version" : "2.4.3-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsmbios",
+ "sourceVersion" : "2.4.3-1build1"
+ },
+ {
+ "name" : "python3-apport",
+ "version" : "2.20.11-0ubuntu82.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apport",
+ "sourceVersion" : "2.20.11-0ubuntu82.1"
+ },
+ {
+ "name" : "lxd-agent-loader",
+ "version" : "0.5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.5"
+ },
+ {
+ "name" : "nano",
+ "version" : "6.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "6.2-1"
+ },
+ {
+ "name" : "console-setup-linux",
+ "version" : "1.205ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "console-setup",
+ "sourceVersion" : "1.205ubuntu3"
+ },
+ {
+ "name" : "gettext-base",
+ "version" : "0.21-4ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gettext",
+ "sourceVersion" : "0.21-4ubuntu4"
+ },
+ {
+ "name" : "libpolkit-gobject-1-0",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceName" : "policykit-1",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "python3-httplib2",
+ "version" : "0.20.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-httplib2",
+ "sourceVersion" : "0.20.2-2"
+ },
+ {
+ "name" : "initramfs-tools-core",
+ "version" : "0.140ubuntu13",
+ "publisher" : "Ubuntu",
+ "sourceName" : "initramfs-tools",
+ "sourceVersion" : "0.140ubuntu13"
+ },
+ {
+ "name" : "libdebconfclient0",
+ "version" : "0.261ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cdebconf",
+ "sourceVersion" : "0.261ubuntu1"
+ },
+ {
+ "name" : "gir1.2-packagekitglib-1.0",
+ "version" : "1.2.5-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "packagekit",
+ "sourceVersion" : "1.2.5-2ubuntu2"
+ },
+ {
+ "name" : "libbsd0",
+ "version" : "0.11.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libbsd",
+ "sourceVersion" : "0.11.5-1"
+ },
+ {
+ "name" : "libunwind8",
+ "version" : "1.3.2-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libunwind",
+ "sourceVersion" : "1.3.2-2build2"
+ },
+ {
+ "name" : "python3-blinker",
+ "version" : "1.4+dfsg1-0.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "blinker",
+ "sourceVersion" : "1.4+dfsg1-0.4"
+ },
+ {
+ "name" : "libc-bin",
+ "version" : "2.35-0ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glibc",
+ "sourceVersion" : "2.35-0ubuntu3"
+ },
+ {
+ "name" : "ncurses-bin",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libcap2-bin",
+ "version" : "1:2.44-1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libcap2",
+ "sourceVersion" : "1:2.44-1build3"
+ },
+ {
+ "name" : "gnupg-l10n",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libeatmydata1",
+ "version" : "130-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libeatmydata",
+ "sourceVersion" : "130-2build1"
+ },
+ {
+ "name" : "libxdmcp6",
+ "version" : "1:1.1.3-0ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxdmcp",
+ "sourceVersion" : "1:1.1.3-0ubuntu5"
+ },
+ {
+ "name" : "libutempter0",
+ "version" : "1.2.1-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libutempter",
+ "sourceVersion" : "1.2.1-2build2"
+ },
+ {
+ "name" : "python3-constantly",
+ "version" : "15.1.0-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "constantly",
+ "sourceVersion" : "15.1.0-2"
+ },
+ {
+ "name" : "libbinutils",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "libstdc++6",
+ "version" : "12-20220319-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gcc-12",
+ "sourceVersion" : "12-20220319-1ubuntu1"
+ },
+ {
+ "name" : "btrfs-progs",
+ "version" : "5.16.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.16.2-1"
+ },
+ {
+ "name" : "python3-pyasn1-modules",
+ "version" : "0.2.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-pyasn1-modules",
+ "sourceVersion" : "0.2.1-1"
+ },
+ {
+ "name" : "python3-six",
+ "version" : "1.16.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "six",
+ "sourceVersion" : "1.16.0-3ubuntu1"
+ },
+ {
+ "name" : "libmpfr6",
+ "version" : "4.1.0-3build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "mpfr4",
+ "sourceVersion" : "4.1.0-3build3"
+ },
+ {
+ "name" : "libtss2-mu0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "run-one",
+ "version" : "1.17-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.17-0ubuntu1"
+ },
+ {
+ "name" : "python3-distro-info",
+ "version" : "1.1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "distro-info",
+ "sourceVersion" : "1.1build1"
+ },
+ {
+ "name" : "libdevmapper1.02.1",
+ "version" : "2:1.2.175-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2 (2.03.11-2.1ubuntu4)",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "python3-cryptography",
+ "version" : "3.4.8-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-cryptography",
+ "sourceVersion" : "3.4.8-1ubuntu2"
+ },
+ {
+ "name" : "libmagic1",
+ "version" : "1:5.41-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "file",
+ "sourceVersion" : "1:5.41-3"
+ },
+ {
+ "name" : "zsh",
+ "version" : "5.8.1-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.8.1-1"
+ },
+ {
+ "name" : "tnftp",
+ "version" : "20210827-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "20210827-4build1"
+ },
+ {
+ "name" : "libsystemd0",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "python3-tz",
+ "version" : "2022.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-tz",
+ "sourceVersion" : "2022.1-1"
+ },
+ {
+ "name" : "libgdbm-compat4",
+ "version" : "1.23-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gdbm",
+ "sourceVersion" : "1.23-1"
+ },
+ {
+ "name" : "libyaml-0-2",
+ "version" : "0.2.2-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libyaml",
+ "sourceVersion" : "0.2.2-1build2"
+ }
+ ],
+ "softwareUpdate" : [
+ {
+ "name" : "snapd",
+ "version" : "2.56.2+22.04ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-software-properties",
+ "version" : "0.99.22.3",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "software-properties-common",
+ "version" : "0.99.22.3",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libldap-common",
+ "version" : "2.5.13+dfsg-0ubuntu0.22.04.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libgstreamer1.0-0",
+ "version" : "1.20.3-0ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup-bin",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup-initramfs",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-distupgrade",
+ "version" : "1:22.04.13",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "ubuntu-release-upgrader-core",
+ "version" : "1:22.04.13",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libnftables1",
+ "version" : "1.0.2-1ubuntu3",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "nftables",
+ "version" : "1.0.2-1ubuntu3",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "ubuntu-advantage-tools",
+ "version" : "27.10.1~22.04.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-gi",
+ "version" : "3.42.1-0ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-apt",
+ "version" : "2.3.0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python-apt-common",
+ "version" : "2.3.0ubuntu2.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "locales",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libnetplan0",
+ "version" : "0.104-0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "netplan.io",
+ "version" : "0.104-0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libcryptsetup12",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "isc-dhcp-common",
+ "version" : "4.4.1-2.3ubuntu2.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "isc-dhcp-client",
+ "version" : "4.4.1-2.3ubuntu2.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "apt-utils",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "apt",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libapt-pkg6.0",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libc-bin",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "base-files",
+ "version" : "12ubuntu4.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libc6",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "motd-news-config",
+ "version" : "12ubuntu4.2",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ }
+ ],
+ "sounds" : [
+ {
+ "name" : "Multimedia audio controller - Intel Corporation 82801AA AC'97 Audio Controller",
+ "description" : "rev 01",
+ "quantity" : 1
+ }
+ ],
+ "storages" : [
+ {
+ "name" : "sda",
+ "description" : "SCSI",
+ "size" : 45035290624,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "serialNumber" : "a23d4170",
+ "sType" : "disk",
+ "quantity" : 1
+ },
+ {
+ "name" : "sdb",
+ "description" : "SCSI",
+ "size" : 10485760,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "sType" : "disk",
+ "quantity" : 1
+ }
+ ],
+ "videos" : [],
+ "vms" : []
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software-no-update-no-process.json_ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software-no-update-no-process.json_
new file mode 100644
index 00000000000..2cbc54de74a
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software-no-update-no-process.json_
@@ -0,0 +1,340 @@
+{
+ "id" : "2d3a43bc-8508-46a2-92d7-cfe7320309a5",
+ "hostname" : "no-software-no-update",
+ "os" : {
+ "type" : "Linux",
+ "name" : "Ubuntu",
+ "version" : "22.04",
+ "fullName" : "Ubuntu 22.04 LTS",
+ "kernelVersion" : "5.15.0-41-generic"
+ },
+ "rudderSettings" : {
+ "keyStatus" : "certified",
+ "reportingConfiguration" : {
+
+ },
+ "kind" : "node",
+ "status" : "pending",
+ "state" : "enabled",
+ "policyMode" : "default",
+ "policyServerId" : "root"
+ },
+ "rudderAgent" : {
+ "type" : "cfengine-community",
+ "user" : "root",
+ "version" : "7.2.0~rc1-ubuntu22.04",
+ "securityToken" : {
+ "kind" : "certificate",
+ "token" : "-----BEGIN CERTIFICATE-----\nMIIFqzCCA5OgAwIBAgIULhYaqmQZGDMab0FnySgkA3tCWpgwDQYJKoZIhvcNAQEL\nBQAwNjE0MDIGCgmSJomT8ixkAQEMJDRkM2E0M2JjLTg1MDgtNDZhMi05MmQ3LWNm\nZTczMjAzMDlhNTAeFw0yMjA5MDcwODUyNDdaFw0zMjA5MDQwODUyNDdaMDYxNDAy\nBgoJkiaJk/IsZAEBDCQ0ZDNhNDNiYy04NTA4LTQ2YTItOTJkNy1jZmU3MzIwMzA5\nYTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDD7/K+BfMAT4pwJf03\nHH9nTBXdDyibg4wbSy0zjiUrQ2Ri5tiwCsfUQtpkeHl3a643FBmHKE2oDvKOvFFV\nrBJWqtJBydVMwhBlDuDXR0WV9fPyIizIcAX/LBt1yPRSrtBkWQZ2IAN/lCADmtEe\ngkVyLiVcp/msMAdwm0QkDvB1o+g3USq0DFbwkQyopvTsYfGgcW/Rn9ifCyxq2rKe\nF+zbUIF7AK5dPjXyDFEEo80WUAZEkn3hQrEKYOCXkEiRLI6+KCArnv9jRop92dCg\nTjGIoMLtAQYb+txytf8/4oQDpCdEENvRJqQ2SVB1K1dtvLPXe7jQ2vK9/chtmFut\ndirZCOBRtbRV0eAZ7te4K0RC5L0R2EI7uowmhE5a8xwmWGH1U7BMVKR9B76iPdR3\nK9fXiPRUEBvs2N/06OBviY18VSR5k4sp9sEifAq3kgP/kvVKTA1dNavoB1gDH8KS\n+0Nnp9nA+AxMLyU5PFFvwSfgYZjp+sbDlFPrOgryVK0BLfGNdm27xftKW8yyzNPM\nbRirrnIngvh8wPHjDxC02/SJ7Pikx0ffOV09ee+Ik4dTbqj+bHy/z8rp0aAtLbZV\nNIGqvzCaByWWJhmYZ3FFwHo3LXUeonljQOYVl8kSEQkcE8b5X657fG3RTrLUE6DK\nobsJcr7JUwijGcB+tADQ+gngtQIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYD\nVR0OBBYEFChVcwIhgIr5vgRy8T8ol3MktitiMHEGA1UdIwRqMGiAFChVcwIhgIr5\nvgRy8T8ol3MktitioTqkODA2MTQwMgYKCZImiZPyLGQBAQwkNGQzYTQzYmMtODUw\nOC00NmEyLTkyZDctY2ZlNzMyMDMwOWE1ghQuFhqqZBkYMxpvQWfJKCQDe0JamDAL\nBgNVHQ8EBAMCArwwDQYJKoZIhvcNAQELBQADggIBAEzLJFdRib1gHQ3ZFU6YCeEX\nUYd8DBRAnPUdjSi/6+Qz9KhQdo5F+vngAdqWFggL2lv1yaahLKTYSdxibBpzX9ee\nH8mP+Gxt0su0xoAtuwkzvfg3G6BTtAXorVgsEfya3re3eShOuvXprzqi7Xsnbv9a\nUzJX4FCJJAXA+Qd/frzpw33cSa8M4SHn7xTo3c9ZgOs1zJ4BepFz/2MpKYY9RSfU\nDpeksnS+ij1BUcrScitwCVvmh0nFXIMWcnUREqxkRAPY1ln1ExAaSmYA0SKsUhoJ\nGx08WfzprwunlzZCA91gmNW+1WjMFr9Nonp6SlMXH0ZrBm+K+zNm6+aAnZdN2ttG\nPmmqotUbbCG737HY8VxE27rgZ8SYidprqvuPtz4WaQPg+4JPeCzOqLWfcX3nfmJM\nkO7nfL0DCqDaZgW+O3FCSFfXnb8TQnMgJZlnakI/w+mzx26YkxtZ2xgWi7yLyBk6\nJDeC5KwgyP+nJpgSOIwGNf4ZaOEYtg3DeFjXmFi5N4Vtq98819cTp3QL+RZG4cVs\nDwFR0T3KjB6QWcvSB2QpQUvdOITtUsACP/zk73y7RwZVQT0DHVWVw6eHhGH6LpKI\nY8skyhHnHUjG1A9eOvk4ZQGPedL9cG0af7TzmaUwFg5fGN594lv79o7iGT1srNBp\n2A/Eff5z/oWypwrLbjiI\n-----END CERTIFICATE-----"
+ },
+ "capabilities" : [
+ "jq",
+ "yaml",
+ "cfengine",
+ "curl",
+ "acl",
+ "xml",
+ "http_reporting"
+ ]
+ },
+ "properties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local",
+ "provider" : "inventory"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override",
+ "provider" : "inventory"
+ }
+ ],
+ "lastInventoryDate" : "2022-09-07T17:56:55Z",
+ "inventoryReceivedDate" : "2022-09-07T17:56:55Z",
+ "ipAddresses" : [
+ "192.168.32.4",
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1",
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "timezone" : {
+ "name" : "UTC",
+ "offset" : "+0000"
+ },
+ "machine" : {
+ "id" : "a9445c97-2640-5736-961f-514e9a34940a",
+ "provider" : "vbox",
+ "systemSerial" : "28eba2ed-b589-ec45-bae4-d33155c8288d",
+ "manufacturer" : "innotek GmbH"
+ },
+ "ram" : 489684992,
+ "archDescription" : "x86_64",
+ "accounts" : [
+ "_apt",
+ "backup",
+ "bin",
+ "daemon",
+ "games",
+ "gnats",
+ "irc",
+ "landscape",
+ "list",
+ "lp",
+ "lxd",
+ "mail",
+ "man",
+ "messagebus",
+ "news",
+ "nobody",
+ "pollinate",
+ "proxy",
+ "root",
+ "sshd",
+ "sync",
+ "sys",
+ "syslog",
+ "systemd-network",
+ "systemd-resolve",
+ "systemd-timesync",
+ "tcpdump",
+ "tss",
+ "ubuntu",
+ "uucp",
+ "uuidd",
+ "vagrant",
+ "www-data"
+ ],
+ "bios" : [
+ {
+ "name" : "VirtualBox",
+ "version" : "VirtualBox",
+ "editor" : "innotek GmbH",
+ "releaseDate" : "2006-11-30T23:00:00Z",
+ "quantity" : 1
+ }
+ ],
+ "controllers" : [
+ {
+ "name" : "440FX - 82441FX PMC [Natoma]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Host bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI",
+ "manufacturer" : "LSI Logic / Symbios Logic",
+ "cType" : "SCSI storage controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 ACPI",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 IDE",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "IDE interface",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371SB PIIX3 ISA [Natoma/Triton II]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "ISA bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82540EM Gigabit Ethernet Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Ethernet controller",
+ "quantity" : 2
+ },
+ {
+ "name" : "82801AA AC'97 Audio Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Multimedia audio controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Graphics Adapter",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "VGA compatible controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Guest Service",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "System peripheral",
+ "quantity" : 1
+ }
+ ],
+ "customProperties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override"
+ }
+ ],
+ "environmentVariables" : [
+ ["BASEDIR", "/opt/rudder/share/commands"],
+ ["DEBIAN_FRONTEND", "noninteractive"],
+ ["HOME", "/root"],
+ ["LESSCLOSE", "/usr/bin/lesspipe %s %s"],
+ ["LESSOPEN", "| /usr/bin/lesspipe %s"],
+ ["LOGNAME", "root"],
+ ["LS_COLORS", "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:"],
+ ["MAIL", "/var/mail/root"],
+ ["PATH", "/opt/rudder/bin:/usr/gnu/bin:/opt/rudder/bin:/usr/gnu/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin:/usr/sbin:/sbin:/usr/sbin"],
+ ["PWD", "/var/rudder"],
+ ["RUDDER_BIN", "/usr/bin/rudder"],
+ ["SHELL", "/bin/bash"],
+ ["SHLVL", "1"],
+ ["SUDO_COMMAND", "/usr/bin/su"],
+ ["SUDO_GID", "1000"],
+ ["SUDO_UID", "1000"],
+ ["SUDO_USER", "vagrant"],
+ ["TERM", "xterm"],
+ ["USER", "root"],
+ ["_", "/usr/bin/rudder"]
+ ],
+ "fileSystems" : [
+ {
+ "mountPoint" : "/",
+ "name" : "ext4",
+ "freeSpace" : 39669727232,
+ "totalSpace" : 41555066880
+ },
+ {
+ "mountPoint" : "/vagrant",
+ "name" : "vboxsf",
+ "freeSpace" : 91722088448,
+ "totalSpace" : 397236240384
+ }
+ ],
+ "inputs" : [],
+ "localGroups" : [],
+ "localUsers" : [],
+ "logicalVolumes" : [],
+ "memories" : [],
+ "networks" : [
+ {
+ "name" : "enp0s3",
+ "ifAddresses" : [
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "ifGateway" : [
+ "10.0.2.2"
+ ],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "10.0.2.0"
+ ],
+ "macAddress" : "02:e5:1b:70:f4:63",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "enp0s8",
+ "ifAddresses" : [
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "192.168.32.4"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "192.168.32.0"
+ ],
+ "macAddress" : "08:00:27:94:72:e8",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "lo",
+ "ifAddresses" : [
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "fff0:0:0:0:0:0:0:0",
+ "255.0.0.0"
+ ],
+ "ifSubnet" : [
+ "0:0:0:0:0:0:0:0",
+ "127.0.0.0"
+ ],
+ "macAddress" : "00:00:00:00:00:00",
+ "status" : "Up",
+ "ifType" : "loopback"
+ }
+ ],
+ "physicalVolumes" : [],
+ "ports" : [],
+ "processes" : [],
+ "processors" : [
+ {
+ "manufacturer" : "Intel",
+ "name" : "Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz",
+ "arch" : "i386",
+ "speed" : 2800,
+ "core" : 1,
+ "thread" : 1,
+ "stepping" : 9,
+ "family" : 6,
+ "model" : 158,
+ "quantity" : 1
+ }
+ ],
+ "slots" : [],
+ "software" : [],
+ "softwareUpdate" : [],
+ "sounds" : [
+ {
+ "name" : "Multimedia audio controller - Intel Corporation 82801AA AC'97 Audio Controller",
+ "description" : "rev 01",
+ "quantity" : 1
+ }
+ ],
+ "storages" : [
+ {
+ "name" : "sda",
+ "description" : "SCSI",
+ "size" : 45035290624,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "serialNumber" : "a23d4170",
+ "sType" : "disk",
+ "quantity" : 1
+ },
+ {
+ "name" : "sdb",
+ "description" : "SCSI",
+ "size" : 10485760,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "sType" : "disk",
+ "quantity" : 1
+ }
+ ],
+ "videos" : [],
+ "vms" : []
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software-no-update.json_ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software-no-update.json_
new file mode 100644
index 00000000000..abd68618635
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software-no-update.json_
@@ -0,0 +1,1571 @@
+{
+ "id" : "2d3a43bc-8508-46a2-92d7-cfe7320309a5",
+ "hostname" : "no-software-no-update",
+ "os" : {
+ "type" : "Linux",
+ "name" : "Ubuntu",
+ "version" : "22.04",
+ "fullName" : "Ubuntu 22.04 LTS",
+ "kernelVersion" : "5.15.0-41-generic"
+ },
+ "rudderSettings" : {
+ "keyStatus" : "certified",
+ "reportingConfiguration" : {
+
+ },
+ "kind" : "node",
+ "status" : "pending",
+ "state" : "enabled",
+ "policyMode" : "default",
+ "policyServerId" : "root"
+ },
+ "rudderAgent" : {
+ "type" : "cfengine-community",
+ "user" : "root",
+ "version" : "7.2.0~rc1-ubuntu22.04",
+ "securityToken" : {
+ "kind" : "certificate",
+ "token" : "-----BEGIN CERTIFICATE-----\nMIIFqzCCA5OgAwIBAgIULhYaqmQZGDMab0FnySgkA3tCWpgwDQYJKoZIhvcNAQEL\nBQAwNjE0MDIGCgmSJomT8ixkAQEMJDRkM2E0M2JjLTg1MDgtNDZhMi05MmQ3LWNm\nZTczMjAzMDlhNTAeFw0yMjA5MDcwODUyNDdaFw0zMjA5MDQwODUyNDdaMDYxNDAy\nBgoJkiaJk/IsZAEBDCQ0ZDNhNDNiYy04NTA4LTQ2YTItOTJkNy1jZmU3MzIwMzA5\nYTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDD7/K+BfMAT4pwJf03\nHH9nTBXdDyibg4wbSy0zjiUrQ2Ri5tiwCsfUQtpkeHl3a643FBmHKE2oDvKOvFFV\nrBJWqtJBydVMwhBlDuDXR0WV9fPyIizIcAX/LBt1yPRSrtBkWQZ2IAN/lCADmtEe\ngkVyLiVcp/msMAdwm0QkDvB1o+g3USq0DFbwkQyopvTsYfGgcW/Rn9ifCyxq2rKe\nF+zbUIF7AK5dPjXyDFEEo80WUAZEkn3hQrEKYOCXkEiRLI6+KCArnv9jRop92dCg\nTjGIoMLtAQYb+txytf8/4oQDpCdEENvRJqQ2SVB1K1dtvLPXe7jQ2vK9/chtmFut\ndirZCOBRtbRV0eAZ7te4K0RC5L0R2EI7uowmhE5a8xwmWGH1U7BMVKR9B76iPdR3\nK9fXiPRUEBvs2N/06OBviY18VSR5k4sp9sEifAq3kgP/kvVKTA1dNavoB1gDH8KS\n+0Nnp9nA+AxMLyU5PFFvwSfgYZjp+sbDlFPrOgryVK0BLfGNdm27xftKW8yyzNPM\nbRirrnIngvh8wPHjDxC02/SJ7Pikx0ffOV09ee+Ik4dTbqj+bHy/z8rp0aAtLbZV\nNIGqvzCaByWWJhmYZ3FFwHo3LXUeonljQOYVl8kSEQkcE8b5X657fG3RTrLUE6DK\nobsJcr7JUwijGcB+tADQ+gngtQIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYD\nVR0OBBYEFChVcwIhgIr5vgRy8T8ol3MktitiMHEGA1UdIwRqMGiAFChVcwIhgIr5\nvgRy8T8ol3MktitioTqkODA2MTQwMgYKCZImiZPyLGQBAQwkNGQzYTQzYmMtODUw\nOC00NmEyLTkyZDctY2ZlNzMyMDMwOWE1ghQuFhqqZBkYMxpvQWfJKCQDe0JamDAL\nBgNVHQ8EBAMCArwwDQYJKoZIhvcNAQELBQADggIBAEzLJFdRib1gHQ3ZFU6YCeEX\nUYd8DBRAnPUdjSi/6+Qz9KhQdo5F+vngAdqWFggL2lv1yaahLKTYSdxibBpzX9ee\nH8mP+Gxt0su0xoAtuwkzvfg3G6BTtAXorVgsEfya3re3eShOuvXprzqi7Xsnbv9a\nUzJX4FCJJAXA+Qd/frzpw33cSa8M4SHn7xTo3c9ZgOs1zJ4BepFz/2MpKYY9RSfU\nDpeksnS+ij1BUcrScitwCVvmh0nFXIMWcnUREqxkRAPY1ln1ExAaSmYA0SKsUhoJ\nGx08WfzprwunlzZCA91gmNW+1WjMFr9Nonp6SlMXH0ZrBm+K+zNm6+aAnZdN2ttG\nPmmqotUbbCG737HY8VxE27rgZ8SYidprqvuPtz4WaQPg+4JPeCzOqLWfcX3nfmJM\nkO7nfL0DCqDaZgW+O3FCSFfXnb8TQnMgJZlnakI/w+mzx26YkxtZ2xgWi7yLyBk6\nJDeC5KwgyP+nJpgSOIwGNf4ZaOEYtg3DeFjXmFi5N4Vtq98819cTp3QL+RZG4cVs\nDwFR0T3KjB6QWcvSB2QpQUvdOITtUsACP/zk73y7RwZVQT0DHVWVw6eHhGH6LpKI\nY8skyhHnHUjG1A9eOvk4ZQGPedL9cG0af7TzmaUwFg5fGN594lv79o7iGT1srNBp\n2A/Eff5z/oWypwrLbjiI\n-----END CERTIFICATE-----"
+ },
+ "capabilities" : [
+ "jq",
+ "yaml",
+ "cfengine",
+ "curl",
+ "acl",
+ "xml",
+ "http_reporting"
+ ]
+ },
+ "properties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local",
+ "provider" : "inventory"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override",
+ "provider" : "inventory"
+ }
+ ],
+ "lastInventoryDate" : "2022-09-07T17:56:55Z",
+ "inventoryReceivedDate" : "2022-09-07T17:56:55Z",
+ "ipAddresses" : [
+ "192.168.32.4",
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1",
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "timezone" : {
+ "name" : "UTC",
+ "offset" : "+0000"
+ },
+ "machine" : {
+ "id" : "a9445c97-2640-5736-961f-514e9a34940a",
+ "provider" : "vbox",
+ "systemSerial" : "28eba2ed-b589-ec45-bae4-d33155c8288d",
+ "manufacturer" : "innotek GmbH"
+ },
+ "ram" : 489684992,
+ "archDescription" : "x86_64",
+ "accounts" : [
+ "_apt",
+ "backup",
+ "bin",
+ "daemon",
+ "games",
+ "gnats",
+ "irc",
+ "landscape",
+ "list",
+ "lp",
+ "lxd",
+ "mail",
+ "man",
+ "messagebus",
+ "news",
+ "nobody",
+ "pollinate",
+ "proxy",
+ "root",
+ "sshd",
+ "sync",
+ "sys",
+ "syslog",
+ "systemd-network",
+ "systemd-resolve",
+ "systemd-timesync",
+ "tcpdump",
+ "tss",
+ "ubuntu",
+ "uucp",
+ "uuidd",
+ "vagrant",
+ "www-data"
+ ],
+ "bios" : [
+ {
+ "name" : "VirtualBox",
+ "version" : "VirtualBox",
+ "editor" : "innotek GmbH",
+ "releaseDate" : "2006-11-30T23:00:00Z",
+ "quantity" : 1
+ }
+ ],
+ "controllers" : [
+ {
+ "name" : "440FX - 82441FX PMC [Natoma]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Host bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI",
+ "manufacturer" : "LSI Logic / Symbios Logic",
+ "cType" : "SCSI storage controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 ACPI",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 IDE",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "IDE interface",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371SB PIIX3 ISA [Natoma/Triton II]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "ISA bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82540EM Gigabit Ethernet Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Ethernet controller",
+ "quantity" : 2
+ },
+ {
+ "name" : "82801AA AC'97 Audio Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Multimedia audio controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Graphics Adapter",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "VGA compatible controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Guest Service",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "System peripheral",
+ "quantity" : 1
+ }
+ ],
+ "customProperties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override"
+ }
+ ],
+ "environmentVariables" : [
+ ["BASEDIR", "/opt/rudder/share/commands"],
+ ["DEBIAN_FRONTEND", "noninteractive"],
+ ["HOME", "/root"],
+ ["LESSCLOSE", "/usr/bin/lesspipe %s %s"],
+ ["LESSOPEN", "| /usr/bin/lesspipe %s"],
+ ["LOGNAME", "root"],
+ ["LS_COLORS", "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:"],
+ ["MAIL", "/var/mail/root"],
+ ["PATH", "/opt/rudder/bin:/usr/gnu/bin:/opt/rudder/bin:/usr/gnu/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin:/usr/sbin:/sbin:/usr/sbin"],
+ ["PWD", "/var/rudder"],
+ ["RUDDER_BIN", "/usr/bin/rudder"],
+ ["SHELL", "/bin/bash"],
+ ["SHLVL", "1"],
+ ["SUDO_COMMAND", "/usr/bin/su"],
+ ["SUDO_GID", "1000"],
+ ["SUDO_UID", "1000"],
+ ["SUDO_USER", "vagrant"],
+ ["TERM", "xterm"],
+ ["USER", "root"],
+ ["_", "/usr/bin/rudder"]
+ ],
+ "fileSystems" : [
+ {
+ "mountPoint" : "/",
+ "name" : "ext4",
+ "freeSpace" : 39669727232,
+ "totalSpace" : 41555066880
+ },
+ {
+ "mountPoint" : "/vagrant",
+ "name" : "vboxsf",
+ "freeSpace" : 91722088448,
+ "totalSpace" : 397236240384
+ }
+ ],
+ "inputs" : [],
+ "localGroups" : [],
+ "localUsers" : [],
+ "logicalVolumes" : [],
+ "memories" : [],
+ "networks" : [
+ {
+ "name" : "enp0s3",
+ "ifAddresses" : [
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "ifGateway" : [
+ "10.0.2.2"
+ ],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "10.0.2.0"
+ ],
+ "macAddress" : "02:e5:1b:70:f4:63",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "enp0s8",
+ "ifAddresses" : [
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "192.168.32.4"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "192.168.32.0"
+ ],
+ "macAddress" : "08:00:27:94:72:e8",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "lo",
+ "ifAddresses" : [
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "fff0:0:0:0:0:0:0:0",
+ "255.0.0.0"
+ ],
+ "ifSubnet" : [
+ "0:0:0:0:0:0:0:0",
+ "127.0.0.0"
+ ],
+ "macAddress" : "00:00:00:00:00:00",
+ "status" : "Up",
+ "ifType" : "loopback"
+ }
+ ],
+ "physicalVolumes" : [],
+ "ports" : [],
+ "processes" : [
+ {
+ "pid" : 24832,
+ "commandName" : "(sd-pam)",
+ "cpuUsage" : 0.0,
+ "memory" : 1.0,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 170396.0
+ },
+ {
+ "pid" : 24915,
+ "commandName" : "-bash",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/0",
+ "user" : "vagrant",
+ "virtualMemory" : 9148.0
+ },
+ {
+ "pid" : 50026,
+ "commandName" : "/bin/sh /opt/rudder/bin/rudder-perl -I /opt/rudder/lib/perl5 /opt/rudder/bin/fusioninventory-agent --config=none --no-task=Deploy --local=/var/rudder/tmp/inventory/node1-4d3a43bc-8508-46a2-92d7-cfe7320309a5.ocs",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 50022,
+ "commandName" : "/bin/sh /opt/rudder/bin/run-inventory --local=/var/rudder/tmp/inventory/node1-4d3a43bc-8508-46a2-92d7-cfe7320309a5.ocs",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49923,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/../lib/timestamp",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49785,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-inventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.3,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49925,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49825,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.4,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 24830,
+ "commandName" : "/lib/systemd/systemd --user",
+ "cpuUsage" : 0.0,
+ "memory" : 1.5,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 17080.0
+ },
+ {
+ "pid" : 339,
+ "commandName" : "/lib/systemd/systemd-journald",
+ "cpuUsage" : 0.0,
+ "memory" : 1.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 48364.0
+ },
+ {
+ "pid" : 683,
+ "commandName" : "/lib/systemd/systemd-logind",
+ "cpuUsage" : 0.0,
+ "memory" : 1.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 23720.0
+ },
+ {
+ "pid" : 1628,
+ "commandName" : "/lib/systemd/systemd-networkd",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:44",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 16248.0
+ },
+ {
+ "pid" : 558,
+ "commandName" : "/lib/systemd/systemd-resolved",
+ "cpuUsage" : 0.0,
+ "memory" : 1.7,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 25392.0
+ },
+ {
+ "pid" : 411,
+ "commandName" : "/lib/systemd/systemd-timesyncd",
+ "cpuUsage" : 0.0,
+ "memory" : 0.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 89352.0
+ },
+ {
+ "pid" : 385,
+ "commandName" : "/lib/systemd/systemd-udevd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.2,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 23304.0
+ },
+ {
+ "pid" : 49921,
+ "commandName" : "/opt/rudder/bin/cf-agent -I -D info -Cnever -K -b doInventory -D force_inventory",
+ "cpuUsage" : 55.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 74680.0
+ },
+ {
+ "pid" : 4493,
+ "commandName" : "/opt/rudder/bin/cf-execd --no-fork",
+ "cpuUsage" : 0.0,
+ "memory" : 2.8,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 33460.0
+ },
+ {
+ "pid" : 4495,
+ "commandName" : "/opt/rudder/bin/cf-serverd --graceful-detach=600 --no-fork --inform",
+ "cpuUsage" : 0.0,
+ "memory" : 2.6,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 33480.0
+ },
+ {
+ "pid" : 746,
+ "commandName" : "/sbin/agetty -o -p -- \\u --keep-baud 115200,57600,38400,9600 ttyS0 vt220",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "ttyS0",
+ "user" : "root",
+ "virtualMemory" : 6216.0
+ },
+ {
+ "pid" : 757,
+ "commandName" : "/sbin/agetty -o -p -- \\u --noclear tty1 linux",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "tty1",
+ "user" : "root",
+ "virtualMemory" : 6172.0
+ },
+ {
+ "pid" : 1,
+ "commandName" : "/sbin/init",
+ "cpuUsage" : 0.0,
+ "memory" : 2.2,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 167620.0
+ },
+ {
+ "pid" : 381,
+ "commandName" : "/sbin/multipathd -d -s",
+ "cpuUsage" : 0.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 354888.0
+ },
+ {
+ "pid" : 674,
+ "commandName" : "/usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers",
+ "cpuUsage" : 0.0,
+ "memory" : 2.3,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 32992.0
+ },
+ {
+ "pid" : 773,
+ "commandName" : "/usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal",
+ "cpuUsage" : 0.0,
+ "memory" : 2.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 110088.0
+ },
+ {
+ "pid" : 681,
+ "commandName" : "/usr/lib/snapd/snapd",
+ "cpuUsage" : 0.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 743408.0
+ },
+ {
+ "pid" : 2287,
+ "commandName" : "/usr/libexec/packagekitd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.8,
+ "started" : "2022-09-07 11:44",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 295940.0
+ },
+ {
+ "pid" : 675,
+ "commandName" : "/usr/libexec/polkitd --no-debug",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 234484.0
+ },
+ {
+ "pid" : 689,
+ "commandName" : "/usr/libexec/udisks2/udisksd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 392700.0
+ },
+ {
+ "pid" : 732,
+ "commandName" : "/usr/sbin/ModemManager",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 316932.0
+ },
+ {
+ "pid" : 665,
+ "commandName" : "/usr/sbin/cron -f -P",
+ "cpuUsage" : 0.0,
+ "memory" : 0.5,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 7284.0
+ },
+ {
+ "pid" : 680,
+ "commandName" : "/usr/sbin/rsyslogd -n -iNONE",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "syslog",
+ "virtualMemory" : 222400.0
+ },
+ {
+ "pid" : 666,
+ "commandName" : "@dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "message+",
+ "virtualMemory" : 8884.0
+ },
+ {
+ "pid" : 87,
+ "commandName" : "[acpi_thermal_pm]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5965,
+ "commandName" : "[arc_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5964,
+ "commandName" : "[arc_prune]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5966,
+ "commandName" : "[arc_reap]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 75,
+ "commandName" : "[ata_sff]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 73,
+ "commandName" : "[blkcg_punt_bio]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 115,
+ "commandName" : "[charger_manager]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 17,
+ "commandName" : "[cpuhp/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 153,
+ "commandName" : "[cryptd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5967,
+ "commandName" : "[dbu_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5968,
+ "commandName" : "[dbuf_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 78,
+ "commandName" : "[devfreq_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5911,
+ "commandName" : "[dio/sda1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 84,
+ "commandName" : "[ecryptfs-kthrea]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 77,
+ "commandName" : "[edac-poller]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 267,
+ "commandName" : "[ext4-rsv-conver]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 16,
+ "commandName" : "[idle_inject/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 19,
+ "commandName" : "[inet_frag_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 362,
+ "commandName" : "[ipmi-msghandler]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 96,
+ "commandName" : "[ipv6_addrconf]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 266,
+ "commandName" : "[jbd2/sda1-8]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 372,
+ "commandName" : "[kaluad]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 20,
+ "commandName" : "[kauditd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 72,
+ "commandName" : "[kblockd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 24,
+ "commandName" : "[kcompactd0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 18,
+ "commandName" : "[kdevtmpfs]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 21,
+ "commandName" : "[khungtaskd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 71,
+ "commandName" : "[kintegrityd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 379,
+ "commandName" : "[kmpath_handlerd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 374,
+ "commandName" : "[kmpath_rdacd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 376,
+ "commandName" : "[kmpathd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 25,
+ "commandName" : "[ksmd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 13,
+ "commandName" : "[ksoftirqd/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 106,
+ "commandName" : "[kstrp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 83,
+ "commandName" : "[kswapd0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 2,
+ "commandName" : "[kthreadd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 86,
+ "commandName" : "[kthrotld]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 32626,
+ "commandName" : "[kworker/0:0-cgroup_destroy]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 16:50",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 7,
+ "commandName" : "[kworker/0:0H-events_highpri]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 81,
+ "commandName" : "[kworker/0:1H-kblockd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 47519,
+ "commandName" : "[kworker/0:2-events]",
+ "cpuUsage" : 0.1,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:34",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 47059,
+ "commandName" : "[kworker/u2:0-writeback]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:32",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 48342,
+ "commandName" : "[kworker/u2:1-ext4-rsv-conversion]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:40",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 48785,
+ "commandName" : "[kworker/u2:2-flush-8:0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 110,
+ "commandName" : "[kworker/u3:0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5970,
+ "commandName" : "[l2arc_feed]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 76,
+ "commandName" : "[md]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 15,
+ "commandName" : "[migration/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 95,
+ "commandName" : "[mld]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 10,
+ "commandName" : "[mm_percpu_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 162,
+ "commandName" : "[mpt/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 161,
+ "commandName" : "[mpt_poll_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5,
+ "commandName" : "[netns]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 22,
+ "commandName" : "[oom_reaper]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 222,
+ "commandName" : "[raid5wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 3,
+ "commandName" : "[rcu_gp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 4,
+ "commandName" : "[rcu_par_gp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 14,
+ "commandName" : "[rcu_sched]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 11,
+ "commandName" : "[rcu_tasks_rude_]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 12,
+ "commandName" : "[rcu_tasks_trace]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 89,
+ "commandName" : "[scsi_eh_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 91,
+ "commandName" : "[scsi_eh_1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 189,
+ "commandName" : "[scsi_eh_2]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 90,
+ "commandName" : "[scsi_tmf_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 92,
+ "commandName" : "[scsi_tmf_1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 190,
+ "commandName" : "[scsi_tmf_2]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5960,
+ "commandName" : "[spl_delay_taskq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5961,
+ "commandName" : "[spl_dynamic_tas]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5962,
+ "commandName" : "[spl_kmem_cache]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5959,
+ "commandName" : "[spl_system_task]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 74,
+ "commandName" : "[tpm_dev_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 94,
+ "commandName" : "[vfio-irqfd-clea]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 79,
+ "commandName" : "[watchdogd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 23,
+ "commandName" : "[writeback]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5969,
+ "commandName" : "[z_vdev_file]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 109,
+ "commandName" : "[zswap-shrink]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5963,
+ "commandName" : "[zvol]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 49926,
+ "commandName" : "awk -v info=0 -v full_strings=0 -v summary_only=0 -v quiet=0 -v multihost=0 -v green=\\033[1;32m -v darkgreen=\\033[0;32m -v red=\\033[1;31m -v yellow=\\033[1;33m -v magenta=\\033[1;35m -v normal=\\033[0;39m\\033[0;49m -v white=\\033[0;02m -v cyan=\\033[1;36m -v dblue=\\033[0;34m -v dgreen=\\033[0;32m -v timing=0 -v has_fflush=OK -v full_compliance=1 -v partial_run=1 -v error_fail=0 -v noncompliant_fail=0 -f /opt/rudder/share/commands/../lib/reports.awk",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 11872.0
+ },
+ {
+ "pid" : 24930,
+ "commandName" : "bash",
+ "cpuUsage" : 0.0,
+ "memory" : 0.7,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 8024.0
+ },
+ {
+ "pid" : 5927,
+ "commandName" : "bpfilter_umh",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2772.0
+ },
+ {
+ "pid" : 49922,
+ "commandName" : "cat",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 6328.0
+ },
+ {
+ "pid" : 50027,
+ "commandName" : "fusioninventory-agent: running task Inventory",
+ "cpuUsage" : 47.0,
+ "memory" : 10.1,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 50496.0
+ },
+ {
+ "pid" : 5878,
+ "commandName" : "lxcfs /var/snap/lxd/common/var/lib/lxcfs -p /var/snap/lxd/common/lxcfs.pid",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 85652.0
+ },
+ {
+ "pid" : 50040,
+ "commandName" : "ps -A -o user,pid,pcpu,pmem,vsz,tty,etime,command",
+ "cpuUsage" : 0.0,
+ "memory" : 0.3,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 7060.0
+ },
+ {
+ "pid" : 797,
+ "commandName" : "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups",
+ "cpuUsage" : 0.0,
+ "memory" : 1.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 15416.0
+ },
+ {
+ "pid" : 24827,
+ "commandName" : "sshd: vagrant [priv]",
+ "cpuUsage" : 0.0,
+ "memory" : 1.3,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 17036.0
+ },
+ {
+ "pid" : 24914,
+ "commandName" : "sshd: vagrant@pts/0",
+ "cpuUsage" : 0.0,
+ "memory" : 1.3,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 17420.0
+ },
+ {
+ "pid" : 24929,
+ "commandName" : "su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.5,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 10592.0
+ },
+ {
+ "pid" : 24928,
+ "commandName" : "sudo su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 11892.0
+ },
+ {
+ "pid" : 24927,
+ "commandName" : "sudo su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.6,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/0",
+ "user" : "root",
+ "virtualMemory" : 11892.0
+ },
+ {
+ "pid" : 49924,
+ "commandName" : "tee /var/rudder/tmp/reports//2022-09-07T19:56:54+00:00@4d3a43bc-8508-46a2-92d7-cfe7320309a5.log",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 6192.0
+ }
+ ],
+ "processors" : [
+ {
+ "manufacturer" : "Intel",
+ "name" : "Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz",
+ "arch" : "i386",
+ "speed" : 2800,
+ "core" : 1,
+ "thread" : 1,
+ "stepping" : 9,
+ "family" : 6,
+ "model" : 158,
+ "quantity" : 1
+ }
+ ],
+ "slots" : [],
+ "software" : [],
+ "softwareUpdate" : [],
+ "sounds" : [
+ {
+ "name" : "Multimedia audio controller - Intel Corporation 82801AA AC'97 Audio Controller",
+ "description" : "rev 01",
+ "quantity" : 1
+ }
+ ],
+ "storages" : [
+ {
+ "name" : "sda",
+ "description" : "SCSI",
+ "size" : 45035290624,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "serialNumber" : "a23d4170",
+ "sType" : "disk",
+ "quantity" : 1
+ },
+ {
+ "name" : "sdb",
+ "description" : "SCSI",
+ "size" : 10485760,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "sType" : "disk",
+ "quantity" : 1
+ }
+ ],
+ "videos" : [],
+ "vms" : []
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software.json_ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software.json_
new file mode 100644
index 00000000000..51d0444d11c
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/no-software.json_
@@ -0,0 +1,1804 @@
+{
+ "id" : "1d3a43bc-8508-46a2-92d7-cfe7320309a5",
+ "hostname" : "no-software",
+ "os" : {
+ "type" : "Linux",
+ "name" : "Ubuntu",
+ "version" : "22.04",
+ "fullName" : "Ubuntu 22.04 LTS",
+ "kernelVersion" : "5.15.0-41-generic"
+ },
+ "rudderSettings" : {
+ "keyStatus" : "certified",
+ "reportingConfiguration" : {
+
+ },
+ "kind" : "node",
+ "status" : "pending",
+ "state" : "enabled",
+ "policyMode" : "default",
+ "policyServerId" : "root"
+ },
+ "rudderAgent" : {
+ "type" : "cfengine-community",
+ "user" : "root",
+ "version" : "7.2.0~rc1-ubuntu22.04",
+ "securityToken" : {
+ "kind" : "certificate",
+ "token" : "-----BEGIN CERTIFICATE-----\nMIIFqzCCA5OgAwIBAgIULhYaqmQZGDMab0FnySgkA3tCWpgwDQYJKoZIhvcNAQEL\nBQAwNjE0MDIGCgmSJomT8ixkAQEMJDRkM2E0M2JjLTg1MDgtNDZhMi05MmQ3LWNm\nZTczMjAzMDlhNTAeFw0yMjA5MDcwODUyNDdaFw0zMjA5MDQwODUyNDdaMDYxNDAy\nBgoJkiaJk/IsZAEBDCQ0ZDNhNDNiYy04NTA4LTQ2YTItOTJkNy1jZmU3MzIwMzA5\nYTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDD7/K+BfMAT4pwJf03\nHH9nTBXdDyibg4wbSy0zjiUrQ2Ri5tiwCsfUQtpkeHl3a643FBmHKE2oDvKOvFFV\nrBJWqtJBydVMwhBlDuDXR0WV9fPyIizIcAX/LBt1yPRSrtBkWQZ2IAN/lCADmtEe\ngkVyLiVcp/msMAdwm0QkDvB1o+g3USq0DFbwkQyopvTsYfGgcW/Rn9ifCyxq2rKe\nF+zbUIF7AK5dPjXyDFEEo80WUAZEkn3hQrEKYOCXkEiRLI6+KCArnv9jRop92dCg\nTjGIoMLtAQYb+txytf8/4oQDpCdEENvRJqQ2SVB1K1dtvLPXe7jQ2vK9/chtmFut\ndirZCOBRtbRV0eAZ7te4K0RC5L0R2EI7uowmhE5a8xwmWGH1U7BMVKR9B76iPdR3\nK9fXiPRUEBvs2N/06OBviY18VSR5k4sp9sEifAq3kgP/kvVKTA1dNavoB1gDH8KS\n+0Nnp9nA+AxMLyU5PFFvwSfgYZjp+sbDlFPrOgryVK0BLfGNdm27xftKW8yyzNPM\nbRirrnIngvh8wPHjDxC02/SJ7Pikx0ffOV09ee+Ik4dTbqj+bHy/z8rp0aAtLbZV\nNIGqvzCaByWWJhmYZ3FFwHo3LXUeonljQOYVl8kSEQkcE8b5X657fG3RTrLUE6DK\nobsJcr7JUwijGcB+tADQ+gngtQIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYD\nVR0OBBYEFChVcwIhgIr5vgRy8T8ol3MktitiMHEGA1UdIwRqMGiAFChVcwIhgIr5\nvgRy8T8ol3MktitioTqkODA2MTQwMgYKCZImiZPyLGQBAQwkNGQzYTQzYmMtODUw\nOC00NmEyLTkyZDctY2ZlNzMyMDMwOWE1ghQuFhqqZBkYMxpvQWfJKCQDe0JamDAL\nBgNVHQ8EBAMCArwwDQYJKoZIhvcNAQELBQADggIBAEzLJFdRib1gHQ3ZFU6YCeEX\nUYd8DBRAnPUdjSi/6+Qz9KhQdo5F+vngAdqWFggL2lv1yaahLKTYSdxibBpzX9ee\nH8mP+Gxt0su0xoAtuwkzvfg3G6BTtAXorVgsEfya3re3eShOuvXprzqi7Xsnbv9a\nUzJX4FCJJAXA+Qd/frzpw33cSa8M4SHn7xTo3c9ZgOs1zJ4BepFz/2MpKYY9RSfU\nDpeksnS+ij1BUcrScitwCVvmh0nFXIMWcnUREqxkRAPY1ln1ExAaSmYA0SKsUhoJ\nGx08WfzprwunlzZCA91gmNW+1WjMFr9Nonp6SlMXH0ZrBm+K+zNm6+aAnZdN2ttG\nPmmqotUbbCG737HY8VxE27rgZ8SYidprqvuPtz4WaQPg+4JPeCzOqLWfcX3nfmJM\nkO7nfL0DCqDaZgW+O3FCSFfXnb8TQnMgJZlnakI/w+mzx26YkxtZ2xgWi7yLyBk6\nJDeC5KwgyP+nJpgSOIwGNf4ZaOEYtg3DeFjXmFi5N4Vtq98819cTp3QL+RZG4cVs\nDwFR0T3KjB6QWcvSB2QpQUvdOITtUsACP/zk73y7RwZVQT0DHVWVw6eHhGH6LpKI\nY8skyhHnHUjG1A9eOvk4ZQGPedL9cG0af7TzmaUwFg5fGN594lv79o7iGT1srNBp\n2A/Eff5z/oWypwrLbjiI\n-----END CERTIFICATE-----"
+ },
+ "capabilities" : [
+ "jq",
+ "yaml",
+ "cfengine",
+ "curl",
+ "acl",
+ "xml",
+ "http_reporting"
+ ]
+ },
+ "properties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local",
+ "provider" : "inventory"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override",
+ "provider" : "inventory"
+ }
+ ],
+ "lastInventoryDate" : "2022-09-07T17:56:55Z",
+ "inventoryReceivedDate" : "2022-09-07T17:56:55Z",
+ "ipAddresses" : [
+ "192.168.32.4",
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1",
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "timezone" : {
+ "name" : "UTC",
+ "offset" : "+0000"
+ },
+ "machine" : {
+ "id" : "a9445c97-2640-5736-961f-514e9a34940a",
+ "provider" : "vbox",
+ "systemSerial" : "28eba2ed-b589-ec45-bae4-d33155c8288d",
+ "manufacturer" : "innotek GmbH"
+ },
+ "ram" : 489684992,
+ "archDescription" : "x86_64",
+ "accounts" : [
+ "_apt",
+ "backup",
+ "bin",
+ "daemon",
+ "games",
+ "gnats",
+ "irc",
+ "landscape",
+ "list",
+ "lp",
+ "lxd",
+ "mail",
+ "man",
+ "messagebus",
+ "news",
+ "nobody",
+ "pollinate",
+ "proxy",
+ "root",
+ "sshd",
+ "sync",
+ "sys",
+ "syslog",
+ "systemd-network",
+ "systemd-resolve",
+ "systemd-timesync",
+ "tcpdump",
+ "tss",
+ "ubuntu",
+ "uucp",
+ "uuidd",
+ "vagrant",
+ "www-data"
+ ],
+ "bios" : [
+ {
+ "name" : "VirtualBox",
+ "version" : "VirtualBox",
+ "editor" : "innotek GmbH",
+ "releaseDate" : "2006-11-30T23:00:00Z",
+ "quantity" : 1
+ }
+ ],
+ "controllers" : [
+ {
+ "name" : "440FX - 82441FX PMC [Natoma]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Host bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI",
+ "manufacturer" : "LSI Logic / Symbios Logic",
+ "cType" : "SCSI storage controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 ACPI",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 IDE",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "IDE interface",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371SB PIIX3 ISA [Natoma/Triton II]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "ISA bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82540EM Gigabit Ethernet Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Ethernet controller",
+ "quantity" : 2
+ },
+ {
+ "name" : "82801AA AC'97 Audio Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Multimedia audio controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Graphics Adapter",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "VGA compatible controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Guest Service",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "System peripheral",
+ "quantity" : 1
+ }
+ ],
+ "customProperties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override"
+ }
+ ],
+ "environmentVariables" : [
+ ["BASEDIR", "/opt/rudder/share/commands"],
+ ["DEBIAN_FRONTEND", "noninteractive"],
+ ["HOME", "/root"],
+ ["LESSCLOSE", "/usr/bin/lesspipe %s %s"],
+ ["LESSOPEN", "| /usr/bin/lesspipe %s"],
+ ["LOGNAME", "root"],
+ ["LS_COLORS", "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:"],
+ ["MAIL", "/var/mail/root"],
+ ["PATH", "/opt/rudder/bin:/usr/gnu/bin:/opt/rudder/bin:/usr/gnu/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin:/usr/sbin:/sbin:/usr/sbin"],
+ ["PWD", "/var/rudder"],
+ ["RUDDER_BIN", "/usr/bin/rudder"],
+ ["SHELL", "/bin/bash"],
+ ["SHLVL", "1"],
+ ["SUDO_COMMAND", "/usr/bin/su"],
+ ["SUDO_GID", "1000"],
+ ["SUDO_UID", "1000"],
+ ["SUDO_USER", "vagrant"],
+ ["TERM", "xterm"],
+ ["USER", "root"],
+ ["_", "/usr/bin/rudder"]
+ ],
+ "fileSystems" : [
+ {
+ "mountPoint" : "/",
+ "name" : "ext4",
+ "freeSpace" : 39669727232,
+ "totalSpace" : 41555066880
+ },
+ {
+ "mountPoint" : "/vagrant",
+ "name" : "vboxsf",
+ "freeSpace" : 91722088448,
+ "totalSpace" : 397236240384
+ }
+ ],
+ "inputs" : [],
+ "localGroups" : [],
+ "localUsers" : [],
+ "logicalVolumes" : [],
+ "memories" : [],
+ "networks" : [
+ {
+ "name" : "enp0s3",
+ "ifAddresses" : [
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "ifGateway" : [
+ "10.0.2.2"
+ ],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "10.0.2.0"
+ ],
+ "macAddress" : "02:e5:1b:70:f4:63",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "enp0s8",
+ "ifAddresses" : [
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "192.168.32.4"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "192.168.32.0"
+ ],
+ "macAddress" : "08:00:27:94:72:e8",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "lo",
+ "ifAddresses" : [
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "fff0:0:0:0:0:0:0:0",
+ "255.0.0.0"
+ ],
+ "ifSubnet" : [
+ "0:0:0:0:0:0:0:0",
+ "127.0.0.0"
+ ],
+ "macAddress" : "00:00:00:00:00:00",
+ "status" : "Up",
+ "ifType" : "loopback"
+ }
+ ],
+ "physicalVolumes" : [],
+ "ports" : [],
+ "processes" : [
+ {
+ "pid" : 24832,
+ "commandName" : "(sd-pam)",
+ "cpuUsage" : 0.0,
+ "memory" : 1.0,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 170396.0
+ },
+ {
+ "pid" : 24915,
+ "commandName" : "-bash",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/0",
+ "user" : "vagrant",
+ "virtualMemory" : 9148.0
+ },
+ {
+ "pid" : 50026,
+ "commandName" : "/bin/sh /opt/rudder/bin/rudder-perl -I /opt/rudder/lib/perl5 /opt/rudder/bin/fusioninventory-agent --config=none --no-task=Deploy --local=/var/rudder/tmp/inventory/node1-4d3a43bc-8508-46a2-92d7-cfe7320309a5.ocs",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 50022,
+ "commandName" : "/bin/sh /opt/rudder/bin/run-inventory --local=/var/rudder/tmp/inventory/node1-4d3a43bc-8508-46a2-92d7-cfe7320309a5.ocs",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49923,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/../lib/timestamp",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49785,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-inventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.3,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49925,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49825,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.4,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 24830,
+ "commandName" : "/lib/systemd/systemd --user",
+ "cpuUsage" : 0.0,
+ "memory" : 1.5,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 17080.0
+ },
+ {
+ "pid" : 339,
+ "commandName" : "/lib/systemd/systemd-journald",
+ "cpuUsage" : 0.0,
+ "memory" : 1.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 48364.0
+ },
+ {
+ "pid" : 683,
+ "commandName" : "/lib/systemd/systemd-logind",
+ "cpuUsage" : 0.0,
+ "memory" : 1.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 23720.0
+ },
+ {
+ "pid" : 1628,
+ "commandName" : "/lib/systemd/systemd-networkd",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:44",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 16248.0
+ },
+ {
+ "pid" : 558,
+ "commandName" : "/lib/systemd/systemd-resolved",
+ "cpuUsage" : 0.0,
+ "memory" : 1.7,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 25392.0
+ },
+ {
+ "pid" : 411,
+ "commandName" : "/lib/systemd/systemd-timesyncd",
+ "cpuUsage" : 0.0,
+ "memory" : 0.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 89352.0
+ },
+ {
+ "pid" : 385,
+ "commandName" : "/lib/systemd/systemd-udevd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.2,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 23304.0
+ },
+ {
+ "pid" : 49921,
+ "commandName" : "/opt/rudder/bin/cf-agent -I -D info -Cnever -K -b doInventory -D force_inventory",
+ "cpuUsage" : 55.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 74680.0
+ },
+ {
+ "pid" : 4493,
+ "commandName" : "/opt/rudder/bin/cf-execd --no-fork",
+ "cpuUsage" : 0.0,
+ "memory" : 2.8,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 33460.0
+ },
+ {
+ "pid" : 4495,
+ "commandName" : "/opt/rudder/bin/cf-serverd --graceful-detach=600 --no-fork --inform",
+ "cpuUsage" : 0.0,
+ "memory" : 2.6,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 33480.0
+ },
+ {
+ "pid" : 746,
+ "commandName" : "/sbin/agetty -o -p -- \\u --keep-baud 115200,57600,38400,9600 ttyS0 vt220",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "ttyS0",
+ "user" : "root",
+ "virtualMemory" : 6216.0
+ },
+ {
+ "pid" : 757,
+ "commandName" : "/sbin/agetty -o -p -- \\u --noclear tty1 linux",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "tty1",
+ "user" : "root",
+ "virtualMemory" : 6172.0
+ },
+ {
+ "pid" : 1,
+ "commandName" : "/sbin/init",
+ "cpuUsage" : 0.0,
+ "memory" : 2.2,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 167620.0
+ },
+ {
+ "pid" : 381,
+ "commandName" : "/sbin/multipathd -d -s",
+ "cpuUsage" : 0.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 354888.0
+ },
+ {
+ "pid" : 674,
+ "commandName" : "/usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers",
+ "cpuUsage" : 0.0,
+ "memory" : 2.3,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 32992.0
+ },
+ {
+ "pid" : 773,
+ "commandName" : "/usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal",
+ "cpuUsage" : 0.0,
+ "memory" : 2.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 110088.0
+ },
+ {
+ "pid" : 681,
+ "commandName" : "/usr/lib/snapd/snapd",
+ "cpuUsage" : 0.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 743408.0
+ },
+ {
+ "pid" : 2287,
+ "commandName" : "/usr/libexec/packagekitd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.8,
+ "started" : "2022-09-07 11:44",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 295940.0
+ },
+ {
+ "pid" : 675,
+ "commandName" : "/usr/libexec/polkitd --no-debug",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 234484.0
+ },
+ {
+ "pid" : 689,
+ "commandName" : "/usr/libexec/udisks2/udisksd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 392700.0
+ },
+ {
+ "pid" : 732,
+ "commandName" : "/usr/sbin/ModemManager",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 316932.0
+ },
+ {
+ "pid" : 665,
+ "commandName" : "/usr/sbin/cron -f -P",
+ "cpuUsage" : 0.0,
+ "memory" : 0.5,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 7284.0
+ },
+ {
+ "pid" : 680,
+ "commandName" : "/usr/sbin/rsyslogd -n -iNONE",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "syslog",
+ "virtualMemory" : 222400.0
+ },
+ {
+ "pid" : 666,
+ "commandName" : "@dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "message+",
+ "virtualMemory" : 8884.0
+ },
+ {
+ "pid" : 87,
+ "commandName" : "[acpi_thermal_pm]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5965,
+ "commandName" : "[arc_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5964,
+ "commandName" : "[arc_prune]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5966,
+ "commandName" : "[arc_reap]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 75,
+ "commandName" : "[ata_sff]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 73,
+ "commandName" : "[blkcg_punt_bio]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 115,
+ "commandName" : "[charger_manager]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 17,
+ "commandName" : "[cpuhp/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 153,
+ "commandName" : "[cryptd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5967,
+ "commandName" : "[dbu_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5968,
+ "commandName" : "[dbuf_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 78,
+ "commandName" : "[devfreq_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5911,
+ "commandName" : "[dio/sda1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 84,
+ "commandName" : "[ecryptfs-kthrea]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 77,
+ "commandName" : "[edac-poller]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 267,
+ "commandName" : "[ext4-rsv-conver]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 16,
+ "commandName" : "[idle_inject/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 19,
+ "commandName" : "[inet_frag_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 362,
+ "commandName" : "[ipmi-msghandler]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 96,
+ "commandName" : "[ipv6_addrconf]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 266,
+ "commandName" : "[jbd2/sda1-8]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 372,
+ "commandName" : "[kaluad]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 20,
+ "commandName" : "[kauditd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 72,
+ "commandName" : "[kblockd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 24,
+ "commandName" : "[kcompactd0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 18,
+ "commandName" : "[kdevtmpfs]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 21,
+ "commandName" : "[khungtaskd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 71,
+ "commandName" : "[kintegrityd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 379,
+ "commandName" : "[kmpath_handlerd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 374,
+ "commandName" : "[kmpath_rdacd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 376,
+ "commandName" : "[kmpathd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 25,
+ "commandName" : "[ksmd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 13,
+ "commandName" : "[ksoftirqd/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 106,
+ "commandName" : "[kstrp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 83,
+ "commandName" : "[kswapd0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 2,
+ "commandName" : "[kthreadd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 86,
+ "commandName" : "[kthrotld]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 32626,
+ "commandName" : "[kworker/0:0-cgroup_destroy]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 16:50",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 7,
+ "commandName" : "[kworker/0:0H-events_highpri]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 81,
+ "commandName" : "[kworker/0:1H-kblockd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 47519,
+ "commandName" : "[kworker/0:2-events]",
+ "cpuUsage" : 0.1,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:34",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 47059,
+ "commandName" : "[kworker/u2:0-writeback]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:32",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 48342,
+ "commandName" : "[kworker/u2:1-ext4-rsv-conversion]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:40",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 48785,
+ "commandName" : "[kworker/u2:2-flush-8:0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 110,
+ "commandName" : "[kworker/u3:0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5970,
+ "commandName" : "[l2arc_feed]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 76,
+ "commandName" : "[md]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 15,
+ "commandName" : "[migration/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 95,
+ "commandName" : "[mld]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 10,
+ "commandName" : "[mm_percpu_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 162,
+ "commandName" : "[mpt/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 161,
+ "commandName" : "[mpt_poll_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5,
+ "commandName" : "[netns]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 22,
+ "commandName" : "[oom_reaper]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 222,
+ "commandName" : "[raid5wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 3,
+ "commandName" : "[rcu_gp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 4,
+ "commandName" : "[rcu_par_gp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 14,
+ "commandName" : "[rcu_sched]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 11,
+ "commandName" : "[rcu_tasks_rude_]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 12,
+ "commandName" : "[rcu_tasks_trace]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 89,
+ "commandName" : "[scsi_eh_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 91,
+ "commandName" : "[scsi_eh_1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 189,
+ "commandName" : "[scsi_eh_2]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 90,
+ "commandName" : "[scsi_tmf_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 92,
+ "commandName" : "[scsi_tmf_1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 190,
+ "commandName" : "[scsi_tmf_2]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5960,
+ "commandName" : "[spl_delay_taskq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5961,
+ "commandName" : "[spl_dynamic_tas]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5962,
+ "commandName" : "[spl_kmem_cache]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5959,
+ "commandName" : "[spl_system_task]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 74,
+ "commandName" : "[tpm_dev_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 94,
+ "commandName" : "[vfio-irqfd-clea]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 79,
+ "commandName" : "[watchdogd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 23,
+ "commandName" : "[writeback]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5969,
+ "commandName" : "[z_vdev_file]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 109,
+ "commandName" : "[zswap-shrink]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5963,
+ "commandName" : "[zvol]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 49926,
+ "commandName" : "awk -v info=0 -v full_strings=0 -v summary_only=0 -v quiet=0 -v multihost=0 -v green=\\033[1;32m -v darkgreen=\\033[0;32m -v red=\\033[1;31m -v yellow=\\033[1;33m -v magenta=\\033[1;35m -v normal=\\033[0;39m\\033[0;49m -v white=\\033[0;02m -v cyan=\\033[1;36m -v dblue=\\033[0;34m -v dgreen=\\033[0;32m -v timing=0 -v has_fflush=OK -v full_compliance=1 -v partial_run=1 -v error_fail=0 -v noncompliant_fail=0 -f /opt/rudder/share/commands/../lib/reports.awk",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 11872.0
+ },
+ {
+ "pid" : 24930,
+ "commandName" : "bash",
+ "cpuUsage" : 0.0,
+ "memory" : 0.7,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 8024.0
+ },
+ {
+ "pid" : 5927,
+ "commandName" : "bpfilter_umh",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2772.0
+ },
+ {
+ "pid" : 49922,
+ "commandName" : "cat",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 6328.0
+ },
+ {
+ "pid" : 50027,
+ "commandName" : "fusioninventory-agent: running task Inventory",
+ "cpuUsage" : 47.0,
+ "memory" : 10.1,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 50496.0
+ },
+ {
+ "pid" : 5878,
+ "commandName" : "lxcfs /var/snap/lxd/common/var/lib/lxcfs -p /var/snap/lxd/common/lxcfs.pid",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 85652.0
+ },
+ {
+ "pid" : 50040,
+ "commandName" : "ps -A -o user,pid,pcpu,pmem,vsz,tty,etime,command",
+ "cpuUsage" : 0.0,
+ "memory" : 0.3,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 7060.0
+ },
+ {
+ "pid" : 797,
+ "commandName" : "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups",
+ "cpuUsage" : 0.0,
+ "memory" : 1.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 15416.0
+ },
+ {
+ "pid" : 24827,
+ "commandName" : "sshd: vagrant [priv]",
+ "cpuUsage" : 0.0,
+ "memory" : 1.3,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 17036.0
+ },
+ {
+ "pid" : 24914,
+ "commandName" : "sshd: vagrant@pts/0",
+ "cpuUsage" : 0.0,
+ "memory" : 1.3,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 17420.0
+ },
+ {
+ "pid" : 24929,
+ "commandName" : "su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.5,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 10592.0
+ },
+ {
+ "pid" : 24928,
+ "commandName" : "sudo su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 11892.0
+ },
+ {
+ "pid" : 24927,
+ "commandName" : "sudo su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.6,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/0",
+ "user" : "root",
+ "virtualMemory" : 11892.0
+ },
+ {
+ "pid" : 49924,
+ "commandName" : "tee /var/rudder/tmp/reports//2022-09-07T19:56:54+00:00@4d3a43bc-8508-46a2-92d7-cfe7320309a5.log",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 6192.0
+ }
+ ],
+ "processors" : [
+ {
+ "manufacturer" : "Intel",
+ "name" : "Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz",
+ "arch" : "i386",
+ "speed" : 2800,
+ "core" : 1,
+ "thread" : 1,
+ "stepping" : 9,
+ "family" : 6,
+ "model" : 158,
+ "quantity" : 1
+ }
+ ],
+ "slots" : [],
+ "software" : [],
+ "softwareUpdate" : [
+ {
+ "name" : "snapd",
+ "version" : "2.56.2+22.04ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-software-properties",
+ "version" : "0.99.22.3",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "software-properties-common",
+ "version" : "0.99.22.3",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libldap-common",
+ "version" : "2.5.13+dfsg-0ubuntu0.22.04.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libgstreamer1.0-0",
+ "version" : "1.20.3-0ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup-bin",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup-initramfs",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-distupgrade",
+ "version" : "1:22.04.13",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "ubuntu-release-upgrader-core",
+ "version" : "1:22.04.13",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libnftables1",
+ "version" : "1.0.2-1ubuntu3",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "nftables",
+ "version" : "1.0.2-1ubuntu3",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "ubuntu-advantage-tools",
+ "version" : "27.10.1~22.04.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-gi",
+ "version" : "3.42.1-0ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-apt",
+ "version" : "2.3.0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python-apt-common",
+ "version" : "2.3.0ubuntu2.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "locales",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libnetplan0",
+ "version" : "0.104-0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "netplan.io",
+ "version" : "0.104-0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libcryptsetup12",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "isc-dhcp-common",
+ "version" : "4.4.1-2.3ubuntu2.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "isc-dhcp-client",
+ "version" : "4.4.1-2.3ubuntu2.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "apt-utils",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "apt",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libapt-pkg6.0",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libc-bin",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "base-files",
+ "version" : "12ubuntu4.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libc6",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "motd-news-config",
+ "version" : "12ubuntu4.2",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ }
+ ],
+ "sounds" : [
+ {
+ "name" : "Multimedia audio controller - Intel Corporation 82801AA AC'97 Audio Controller",
+ "description" : "rev 01",
+ "quantity" : 1
+ }
+ ],
+ "storages" : [
+ {
+ "name" : "sda",
+ "description" : "SCSI",
+ "size" : 45035290624,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "serialNumber" : "a23d4170",
+ "sType" : "disk",
+ "quantity" : 1
+ },
+ {
+ "name" : "sdb",
+ "description" : "SCSI",
+ "size" : 10485760,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "sType" : "disk",
+ "quantity" : 1
+ }
+ ],
+ "videos" : [],
+ "vms" : []
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/root.json b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/root.json
new file mode 100644
index 00000000000..32320cd8fc3
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/resources/node-facts/root.json
@@ -0,0 +1,5877 @@
+{
+ "id" : "root",
+ "hostname" : "root.rudder.local",
+ "os" : {
+ "type" : "Linux",
+ "name" : "Ubuntu",
+ "version" : "22.04",
+ "fullName" : "Ubuntu 22.04 LTS",
+ "kernelVersion" : "5.15.0-41-generic"
+ },
+ "rudderSettings" : {
+ "keyStatus" : "certified",
+ "reportingConfiguration" : {
+
+ },
+ "kind" : "root",
+ "status" : "accepted",
+ "state" : "enabled",
+ "policyMode" : "default",
+ "policyServerId" : "root"
+ },
+ "rudderAgent" : {
+ "type" : "cfengine-community",
+ "user" : "root",
+ "version" : "7.2.0-ubuntu22.04",
+ "securityToken" : {
+ "kind" : "certificate",
+ "token" : "-----BEGIN CERTIFICATE-----\nMIIFqzCCA5OgAwIBAgIULhYaqmQZGDMab0FnySgkA3tCWpgwDQYJKoZIhvcNAQEL\nBQAwNjE0MDIGCgmSJomT8ixkAQEMJDRkM2E0M2JjLTg1MDgtNDZhMi05MmQ3LWNm\nZTczMjAzMDlhNTAeFw0yMjA5MDcwODUyNDdaFw0zMjA5MDQwODUyNDdaMDYxNDAy\nBgoJkiaJk/IsZAEBDCQ0ZDNhNDNiYy04NTA4LTQ2YTItOTJkNy1jZmU3MzIwMzA5\nYTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDD7/K+BfMAT4pwJf03\nHH9nTBXdDyibg4wbSy0zjiUrQ2Ri5tiwCsfUQtpkeHl3a643FBmHKE2oDvKOvFFV\nrBJWqtJBydVMwhBlDuDXR0WV9fPyIizIcAX/LBt1yPRSrtBkWQZ2IAN/lCADmtEe\ngkVyLiVcp/msMAdwm0QkDvB1o+g3USq0DFbwkQyopvTsYfGgcW/Rn9ifCyxq2rKe\nF+zbUIF7AK5dPjXyDFEEo80WUAZEkn3hQrEKYOCXkEiRLI6+KCArnv9jRop92dCg\nTjGIoMLtAQYb+txytf8/4oQDpCdEENvRJqQ2SVB1K1dtvLPXe7jQ2vK9/chtmFut\ndirZCOBRtbRV0eAZ7te4K0RC5L0R2EI7uowmhE5a8xwmWGH1U7BMVKR9B76iPdR3\nK9fXiPRUEBvs2N/06OBviY18VSR5k4sp9sEifAq3kgP/kvVKTA1dNavoB1gDH8KS\n+0Nnp9nA+AxMLyU5PFFvwSfgYZjp+sbDlFPrOgryVK0BLfGNdm27xftKW8yyzNPM\nbRirrnIngvh8wPHjDxC02/SJ7Pikx0ffOV09ee+Ik4dTbqj+bHy/z8rp0aAtLbZV\nNIGqvzCaByWWJhmYZ3FFwHo3LXUeonljQOYVl8kSEQkcE8b5X657fG3RTrLUE6DK\nobsJcr7JUwijGcB+tADQ+gngtQIDAQABo4GwMIGtMAwGA1UdEwQFMAMBAf8wHQYD\nVR0OBBYEFChVcwIhgIr5vgRy8T8ol3MktitiMHEGA1UdIwRqMGiAFChVcwIhgIr5\nvgRy8T8ol3MktitioTqkODA2MTQwMgYKCZImiZPyLGQBAQwkNGQzYTQzYmMtODUw\nOC00NmEyLTkyZDctY2ZlNzMyMDMwOWE1ghQuFhqqZBkYMxpvQWfJKCQDe0JamDAL\nBgNVHQ8EBAMCArwwDQYJKoZIhvcNAQELBQADggIBAEzLJFdRib1gHQ3ZFU6YCeEX\nUYd8DBRAnPUdjSi/6+Qz9KhQdo5F+vngAdqWFggL2lv1yaahLKTYSdxibBpzX9ee\nH8mP+Gxt0su0xoAtuwkzvfg3G6BTtAXorVgsEfya3re3eShOuvXprzqi7Xsnbv9a\nUzJX4FCJJAXA+Qd/frzpw33cSa8M4SHn7xTo3c9ZgOs1zJ4BepFz/2MpKYY9RSfU\nDpeksnS+ij1BUcrScitwCVvmh0nFXIMWcnUREqxkRAPY1ln1ExAaSmYA0SKsUhoJ\nGx08WfzprwunlzZCA91gmNW+1WjMFr9Nonp6SlMXH0ZrBm+K+zNm6+aAnZdN2ttG\nPmmqotUbbCG737HY8VxE27rgZ8SYidprqvuPtz4WaQPg+4JPeCzOqLWfcX3nfmJM\nkO7nfL0DCqDaZgW+O3FCSFfXnb8TQnMgJZlnakI/w+mzx26YkxtZ2xgWi7yLyBk6\nJDeC5KwgyP+nJpgSOIwGNf4ZaOEYtg3DeFjXmFi5N4Vtq98819cTp3QL+RZG4cVs\nDwFR0T3KjB6QWcvSB2QpQUvdOITtUsACP/zk73y7RwZVQT0DHVWVw6eHhGH6LpKI\nY8skyhHnHUjG1A9eOvk4ZQGPedL9cG0af7TzmaUwFg5fGN594lv79o7iGT1srNBp\n2A/Eff5z/oWypwrLbjiI\n-----END CERTIFICATE-----"
+ },
+ "capabilities" : [
+ "jq",
+ "yaml",
+ "cfengine",
+ "curl",
+ "acl",
+ "xml",
+ "http_reporting"
+ ]
+ },
+ "properties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local",
+ "provider" : "inventory"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override",
+ "provider" : "inventory"
+ }
+ ],
+ "lastInventoryDate" : "2022-09-07T17:56:55Z",
+ "inventoryReceivedDate" : "2022-09-07T17:56:55Z",
+ "ipAddresses" : [
+ "192.168.32.4",
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1",
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "timezone" : {
+ "name" : "UTC",
+ "offset" : "+0000"
+ },
+ "machine" : {
+ "id" : "a9445c97-2640-5736-961f-514e9a34940a",
+ "provider" : "vbox",
+ "systemSerial" : "28eba2ed-b589-ec45-bae4-d33155c8288d",
+ "manufacturer" : "innotek GmbH"
+ },
+ "ram" : 489684992,
+ "archDescription" : "x86_64",
+ "accounts" : [
+ "_apt",
+ "backup",
+ "bin",
+ "daemon",
+ "games",
+ "gnats",
+ "irc",
+ "landscape",
+ "list",
+ "lp",
+ "lxd",
+ "mail",
+ "man",
+ "messagebus",
+ "news",
+ "nobody",
+ "pollinate",
+ "proxy",
+ "root",
+ "sshd",
+ "sync",
+ "sys",
+ "syslog",
+ "systemd-network",
+ "systemd-resolve",
+ "systemd-timesync",
+ "tcpdump",
+ "tss",
+ "ubuntu",
+ "uucp",
+ "uuidd",
+ "vagrant",
+ "www-data"
+ ],
+ "bios" : [
+ {
+ "name" : "VirtualBox",
+ "version" : "VirtualBox",
+ "editor" : "innotek GmbH",
+ "releaseDate" : "2006-11-30T23:00:00Z",
+ "quantity" : 1
+ }
+ ],
+ "controllers" : [
+ {
+ "name" : "440FX - 82441FX PMC [Natoma]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Host bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI",
+ "manufacturer" : "LSI Logic / Symbios Logic",
+ "cType" : "SCSI storage controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 ACPI",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371AB/EB/MB PIIX4 IDE",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "IDE interface",
+ "quantity" : 1
+ },
+ {
+ "name" : "82371SB PIIX3 ISA [Natoma/Triton II]",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "ISA bridge",
+ "quantity" : 1
+ },
+ {
+ "name" : "82540EM Gigabit Ethernet Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Ethernet controller",
+ "quantity" : 2
+ },
+ {
+ "name" : "82801AA AC'97 Audio Controller",
+ "manufacturer" : "Intel Corporation",
+ "cType" : "Multimedia audio controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Graphics Adapter",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "VGA compatible controller",
+ "quantity" : 1
+ },
+ {
+ "name" : "VirtualBox Guest Service",
+ "manufacturer" : "InnoTek Systemberatung GmbH",
+ "cType" : "System peripheral",
+ "quantity" : 1
+ }
+ ],
+ "customProperties" : [
+ {
+ "name" : "rudder_original_hostname",
+ "value" : "node1.rudder.local"
+ },
+ {
+ "name" : "rudder_override_hostname",
+ "value" : "node1-overridden.rudder.local.override"
+ }
+ ],
+ "environmentVariables" : [
+ ["BASEDIR", "/opt/rudder/share/commands"],
+ ["DEBIAN_FRONTEND", "noninteractive"],
+ ["HOME", "/root"],
+ ["LESSCLOSE", "/usr/bin/lesspipe %s %s"],
+ ["LESSOPEN", "| /usr/bin/lesspipe %s"],
+ ["LOGNAME", "root"],
+ ["LS_COLORS", "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:"],
+ ["MAIL", "/var/mail/root"],
+ ["PATH", "/opt/rudder/bin:/usr/gnu/bin:/opt/rudder/bin:/usr/gnu/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/sbin:/usr/sbin:/sbin:/usr/sbin"],
+ ["PWD", "/var/rudder"],
+ ["RUDDER_BIN", "/usr/bin/rudder"],
+ ["SHELL", "/bin/bash"],
+ ["SHLVL", "1"],
+ ["SUDO_COMMAND", "/usr/bin/su"],
+ ["SUDO_GID", "1000"],
+ ["SUDO_UID", "1000"],
+ ["SUDO_USER", "vagrant"],
+ ["TERM", "xterm"],
+ ["USER", "root"],
+ ["_", "/usr/bin/rudder"]
+ ],
+ "fileSystems" : [
+ {
+ "mountPoint" : "/",
+ "name" : "ext4",
+ "freeSpace" : 39669727232,
+ "totalSpace" : 41555066880
+ },
+ {
+ "mountPoint" : "/vagrant",
+ "name" : "vboxsf",
+ "freeSpace" : 91722088448,
+ "totalSpace" : 397236240384
+ }
+ ],
+ "inputs" : [],
+ "localGroups" : [],
+ "localUsers" : [],
+ "logicalVolumes" : [],
+ "memories" : [],
+ "networks" : [
+ {
+ "name" : "enp0s3",
+ "ifAddresses" : [
+ "fe80:0:0:0:e5:1bff:fe70:f463",
+ "10.0.2.15"
+ ],
+ "ifGateway" : [
+ "10.0.2.2"
+ ],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "10.0.2.0"
+ ],
+ "macAddress" : "02:e5:1b:70:f4:63",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "enp0s8",
+ "ifAddresses" : [
+ "fe80:0:0:0:a00:27ff:fe94:72e8",
+ "192.168.32.4"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "ffff:ffff:ffff:ffff:0:0:0:0",
+ "255.255.255.0"
+ ],
+ "ifSubnet" : [
+ "fe80:0:0:0:0:0:0:0",
+ "192.168.32.0"
+ ],
+ "macAddress" : "08:00:27:94:72:e8",
+ "status" : "Up",
+ "ifType" : "ethernet",
+ "speed" : "1000"
+ },
+ {
+ "name" : "lo",
+ "ifAddresses" : [
+ "0:0:0:0:0:0:0:1",
+ "127.0.0.1"
+ ],
+ "ifGateway" : [],
+ "ifMask" : [
+ "fff0:0:0:0:0:0:0:0",
+ "255.0.0.0"
+ ],
+ "ifSubnet" : [
+ "0:0:0:0:0:0:0:0",
+ "127.0.0.0"
+ ],
+ "macAddress" : "00:00:00:00:00:00",
+ "status" : "Up",
+ "ifType" : "loopback"
+ }
+ ],
+ "physicalVolumes" : [],
+ "ports" : [],
+ "processes" : [
+ {
+ "pid" : 24832,
+ "commandName" : "(sd-pam)",
+ "cpuUsage" : 0.0,
+ "memory" : 1.0,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 170396.0
+ },
+ {
+ "pid" : 24915,
+ "commandName" : "-bash",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/0",
+ "user" : "vagrant",
+ "virtualMemory" : 9148.0
+ },
+ {
+ "pid" : 50026,
+ "commandName" : "/bin/sh /opt/rudder/bin/rudder-perl -I /opt/rudder/lib/perl5 /opt/rudder/bin/fusioninventory-agent --config=none --no-task=Deploy --local=/var/rudder/tmp/inventory/node1-root.ocs",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 50022,
+ "commandName" : "/bin/sh /opt/rudder/bin/run-inventory --local=/var/rudder/tmp/inventory/node1-root.ocs",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49923,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/../lib/timestamp",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49785,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-inventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.3,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49925,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 49825,
+ "commandName" : "/bin/sh /opt/rudder/share/commands/agent-run -N -D force_inventory -b doInventory",
+ "cpuUsage" : 0.0,
+ "memory" : 0.4,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 2888.0
+ },
+ {
+ "pid" : 24830,
+ "commandName" : "/lib/systemd/systemd --user",
+ "cpuUsage" : 0.0,
+ "memory" : 1.5,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 17080.0
+ },
+ {
+ "pid" : 339,
+ "commandName" : "/lib/systemd/systemd-journald",
+ "cpuUsage" : 0.0,
+ "memory" : 1.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 48364.0
+ },
+ {
+ "pid" : 683,
+ "commandName" : "/lib/systemd/systemd-logind",
+ "cpuUsage" : 0.0,
+ "memory" : 1.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 23720.0
+ },
+ {
+ "pid" : 1628,
+ "commandName" : "/lib/systemd/systemd-networkd",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:44",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 16248.0
+ },
+ {
+ "pid" : 558,
+ "commandName" : "/lib/systemd/systemd-resolved",
+ "cpuUsage" : 0.0,
+ "memory" : 1.7,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 25392.0
+ },
+ {
+ "pid" : 411,
+ "commandName" : "/lib/systemd/systemd-timesyncd",
+ "cpuUsage" : 0.0,
+ "memory" : 0.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "systemd+",
+ "virtualMemory" : 89352.0
+ },
+ {
+ "pid" : 385,
+ "commandName" : "/lib/systemd/systemd-udevd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.2,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 23304.0
+ },
+ {
+ "pid" : 49921,
+ "commandName" : "/opt/rudder/bin/cf-agent -I -D info -Cnever -K -b doInventory -D force_inventory",
+ "cpuUsage" : 55.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 74680.0
+ },
+ {
+ "pid" : 4493,
+ "commandName" : "/opt/rudder/bin/cf-execd --no-fork",
+ "cpuUsage" : 0.0,
+ "memory" : 2.8,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 33460.0
+ },
+ {
+ "pid" : 4495,
+ "commandName" : "/opt/rudder/bin/cf-serverd --graceful-detach=600 --no-fork --inform",
+ "cpuUsage" : 0.0,
+ "memory" : 2.6,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 33480.0
+ },
+ {
+ "pid" : 746,
+ "commandName" : "/sbin/agetty -o -p -- \\u --keep-baud 115200,57600,38400,9600 ttyS0 vt220",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "ttyS0",
+ "user" : "root",
+ "virtualMemory" : 6216.0
+ },
+ {
+ "pid" : 757,
+ "commandName" : "/sbin/agetty -o -p -- \\u --noclear tty1 linux",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "tty1",
+ "user" : "root",
+ "virtualMemory" : 6172.0
+ },
+ {
+ "pid" : 1,
+ "commandName" : "/sbin/init",
+ "cpuUsage" : 0.0,
+ "memory" : 2.2,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 167620.0
+ },
+ {
+ "pid" : 381,
+ "commandName" : "/sbin/multipathd -d -s",
+ "cpuUsage" : 0.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 354888.0
+ },
+ {
+ "pid" : 674,
+ "commandName" : "/usr/bin/python3 /usr/bin/networkd-dispatcher --run-startup-triggers",
+ "cpuUsage" : 0.0,
+ "memory" : 2.3,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 32992.0
+ },
+ {
+ "pid" : 773,
+ "commandName" : "/usr/bin/python3 /usr/share/unattended-upgrades/unattended-upgrade-shutdown --wait-for-signal",
+ "cpuUsage" : 0.0,
+ "memory" : 2.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 110088.0
+ },
+ {
+ "pid" : 681,
+ "commandName" : "/usr/lib/snapd/snapd",
+ "cpuUsage" : 0.0,
+ "memory" : 5.6,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 743408.0
+ },
+ {
+ "pid" : 2287,
+ "commandName" : "/usr/libexec/packagekitd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.8,
+ "started" : "2022-09-07 11:44",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 295940.0
+ },
+ {
+ "pid" : 675,
+ "commandName" : "/usr/libexec/polkitd --no-debug",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 234484.0
+ },
+ {
+ "pid" : 689,
+ "commandName" : "/usr/libexec/udisks2/udisksd",
+ "cpuUsage" : 0.0,
+ "memory" : 1.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 392700.0
+ },
+ {
+ "pid" : 732,
+ "commandName" : "/usr/sbin/ModemManager",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 316932.0
+ },
+ {
+ "pid" : 665,
+ "commandName" : "/usr/sbin/cron -f -P",
+ "cpuUsage" : 0.0,
+ "memory" : 0.5,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 7284.0
+ },
+ {
+ "pid" : 680,
+ "commandName" : "/usr/sbin/rsyslogd -n -iNONE",
+ "cpuUsage" : 0.0,
+ "memory" : 0.9,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "syslog",
+ "virtualMemory" : 222400.0
+ },
+ {
+ "pid" : 666,
+ "commandName" : "@dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "message+",
+ "virtualMemory" : 8884.0
+ },
+ {
+ "pid" : 87,
+ "commandName" : "[acpi_thermal_pm]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5965,
+ "commandName" : "[arc_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5964,
+ "commandName" : "[arc_prune]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5966,
+ "commandName" : "[arc_reap]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 75,
+ "commandName" : "[ata_sff]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 73,
+ "commandName" : "[blkcg_punt_bio]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 115,
+ "commandName" : "[charger_manager]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 17,
+ "commandName" : "[cpuhp/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 153,
+ "commandName" : "[cryptd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5967,
+ "commandName" : "[dbu_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5968,
+ "commandName" : "[dbuf_evict]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 78,
+ "commandName" : "[devfreq_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5911,
+ "commandName" : "[dio/sda1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 84,
+ "commandName" : "[ecryptfs-kthrea]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 77,
+ "commandName" : "[edac-poller]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 267,
+ "commandName" : "[ext4-rsv-conver]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 16,
+ "commandName" : "[idle_inject/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 19,
+ "commandName" : "[inet_frag_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 362,
+ "commandName" : "[ipmi-msghandler]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 96,
+ "commandName" : "[ipv6_addrconf]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 266,
+ "commandName" : "[jbd2/sda1-8]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 372,
+ "commandName" : "[kaluad]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 20,
+ "commandName" : "[kauditd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 72,
+ "commandName" : "[kblockd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 24,
+ "commandName" : "[kcompactd0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 18,
+ "commandName" : "[kdevtmpfs]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 21,
+ "commandName" : "[khungtaskd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 71,
+ "commandName" : "[kintegrityd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 379,
+ "commandName" : "[kmpath_handlerd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 374,
+ "commandName" : "[kmpath_rdacd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 376,
+ "commandName" : "[kmpathd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 25,
+ "commandName" : "[ksmd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 13,
+ "commandName" : "[ksoftirqd/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 106,
+ "commandName" : "[kstrp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 83,
+ "commandName" : "[kswapd0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 2,
+ "commandName" : "[kthreadd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 86,
+ "commandName" : "[kthrotld]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 32626,
+ "commandName" : "[kworker/0:0-cgroup_destroy]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 16:50",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 7,
+ "commandName" : "[kworker/0:0H-events_highpri]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 81,
+ "commandName" : "[kworker/0:1H-kblockd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 47519,
+ "commandName" : "[kworker/0:2-events]",
+ "cpuUsage" : 0.1,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:34",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 47059,
+ "commandName" : "[kworker/u2:0-writeback]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:32",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 48342,
+ "commandName" : "[kworker/u2:1-ext4-rsv-conversion]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:40",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 48785,
+ "commandName" : "[kworker/u2:2-flush-8:0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 19:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 110,
+ "commandName" : "[kworker/u3:0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5970,
+ "commandName" : "[l2arc_feed]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 76,
+ "commandName" : "[md]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 15,
+ "commandName" : "[migration/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 95,
+ "commandName" : "[mld]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 10,
+ "commandName" : "[mm_percpu_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 162,
+ "commandName" : "[mpt/0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 161,
+ "commandName" : "[mpt_poll_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5,
+ "commandName" : "[netns]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 22,
+ "commandName" : "[oom_reaper]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 222,
+ "commandName" : "[raid5wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 3,
+ "commandName" : "[rcu_gp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 4,
+ "commandName" : "[rcu_par_gp]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 14,
+ "commandName" : "[rcu_sched]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 11,
+ "commandName" : "[rcu_tasks_rude_]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 12,
+ "commandName" : "[rcu_tasks_trace]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 89,
+ "commandName" : "[scsi_eh_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 91,
+ "commandName" : "[scsi_eh_1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 189,
+ "commandName" : "[scsi_eh_2]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 90,
+ "commandName" : "[scsi_tmf_0]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 92,
+ "commandName" : "[scsi_tmf_1]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 190,
+ "commandName" : "[scsi_tmf_2]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5960,
+ "commandName" : "[spl_delay_taskq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5961,
+ "commandName" : "[spl_dynamic_tas]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5962,
+ "commandName" : "[spl_kmem_cache]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5959,
+ "commandName" : "[spl_system_task]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 74,
+ "commandName" : "[tpm_dev_wq]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 94,
+ "commandName" : "[vfio-irqfd-clea]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 79,
+ "commandName" : "[watchdogd]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 23,
+ "commandName" : "[writeback]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5969,
+ "commandName" : "[z_vdev_file]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 109,
+ "commandName" : "[zswap-shrink]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 5963,
+ "commandName" : "[zvol]",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 0.0
+ },
+ {
+ "pid" : 49926,
+ "commandName" : "awk -v info=0 -v full_strings=0 -v summary_only=0 -v quiet=0 -v multihost=0 -v green=\\033[1;32m -v darkgreen=\\033[0;32m -v red=\\033[1;31m -v yellow=\\033[1;33m -v magenta=\\033[1;35m -v normal=\\033[0;39m\\033[0;49m -v white=\\033[0;02m -v cyan=\\033[1;36m -v dblue=\\033[0;34m -v dgreen=\\033[0;32m -v timing=0 -v has_fflush=OK -v full_compliance=1 -v partial_run=1 -v error_fail=0 -v noncompliant_fail=0 -f /opt/rudder/share/commands/../lib/reports.awk",
+ "cpuUsage" : 0.0,
+ "memory" : 0.8,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 11872.0
+ },
+ {
+ "pid" : 24930,
+ "commandName" : "bash",
+ "cpuUsage" : 0.0,
+ "memory" : 0.7,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 8024.0
+ },
+ {
+ "pid" : 5927,
+ "commandName" : "bpfilter_umh",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 2772.0
+ },
+ {
+ "pid" : 49922,
+ "commandName" : "cat",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 6328.0
+ },
+ {
+ "pid" : 50027,
+ "commandName" : "fusioninventory-agent: running task Inventory",
+ "cpuUsage" : 47.0,
+ "memory" : 10.1,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 50496.0
+ },
+ {
+ "pid" : 5878,
+ "commandName" : "lxcfs /var/snap/lxd/common/var/lib/lxcfs -p /var/snap/lxd/common/lxcfs.pid",
+ "cpuUsage" : 0.0,
+ "memory" : 0.0,
+ "started" : "2022-09-07 11:45",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 85652.0
+ },
+ {
+ "pid" : 50040,
+ "commandName" : "ps -A -o user,pid,pcpu,pmem,vsz,tty,etime,command",
+ "cpuUsage" : 0.0,
+ "memory" : 0.3,
+ "started" : "2022-09-07 19:56",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 7060.0
+ },
+ {
+ "pid" : 797,
+ "commandName" : "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups",
+ "cpuUsage" : 0.0,
+ "memory" : 1.1,
+ "started" : "2022-09-07 11:43",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 15416.0
+ },
+ {
+ "pid" : 24827,
+ "commandName" : "sshd: vagrant [priv]",
+ "cpuUsage" : 0.0,
+ "memory" : 1.3,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "root",
+ "virtualMemory" : 17036.0
+ },
+ {
+ "pid" : 24914,
+ "commandName" : "sshd: vagrant@pts/0",
+ "cpuUsage" : 0.0,
+ "memory" : 1.3,
+ "started" : "2022-09-07 15:30",
+ "tty" : "?",
+ "user" : "vagrant",
+ "virtualMemory" : 17420.0
+ },
+ {
+ "pid" : 24929,
+ "commandName" : "su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.5,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 10592.0
+ },
+ {
+ "pid" : 24928,
+ "commandName" : "sudo su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.1,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 11892.0
+ },
+ {
+ "pid" : 24927,
+ "commandName" : "sudo su",
+ "cpuUsage" : 0.0,
+ "memory" : 0.6,
+ "started" : "2022-09-07 15:30",
+ "tty" : "pts/0",
+ "user" : "root",
+ "virtualMemory" : 11892.0
+ },
+ {
+ "pid" : 49924,
+ "commandName" : "tee /var/rudder/tmp/reports//2022-09-07T19:56:54+00:00@root.log",
+ "cpuUsage" : 0.0,
+ "memory" : 0.2,
+ "started" : "2022-09-07 19:56",
+ "tty" : "pts/1",
+ "user" : "root",
+ "virtualMemory" : 6192.0
+ }
+ ],
+ "processors" : [
+ {
+ "manufacturer" : "Intel",
+ "name" : "Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz",
+ "arch" : "i386",
+ "speed" : 2800,
+ "core" : 1,
+ "thread" : 1,
+ "stepping" : 9,
+ "family" : 6,
+ "model" : 158,
+ "quantity" : 1
+ }
+ ],
+ "slots" : [],
+ "software" : [
+ {
+ "name" : "libassuan0",
+ "version" : "2.5.5-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libassuan",
+ "sourceVersion" : "2.5.5-1build1"
+ },
+ {
+ "name" : "libbrotli1",
+ "version" : "1.0.9-2build6",
+ "publisher" : "Ubuntu",
+ "sourceName" : "brotli",
+ "sourceVersion" : "1.0.9-2build6"
+ },
+ {
+ "name" : "pastebinit",
+ "version" : "1.5.1-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.5.1-1ubuntu1"
+ },
+ {
+ "name" : "cryptsetup",
+ "version" : "2:2.4.3-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2:2.4.3-1ubuntu1"
+ },
+ {
+ "name" : "libstemmer0d",
+ "version" : "2.2.0-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "snowball",
+ "sourceVersion" : "2.2.0-1build1"
+ },
+ {
+ "name" : "python3-zope.interface",
+ "version" : "5.4.0-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "zope.interface",
+ "sourceVersion" : "5.4.0-1build1"
+ },
+ {
+ "name" : "libedit2",
+ "version" : "3.1-20210910-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libedit",
+ "sourceVersion" : "3.1-20210910-1build1"
+ },
+ {
+ "name" : "tcl8.6",
+ "version" : "8.6.12+dfsg-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "8.6.12+dfsg-1build1"
+ },
+ {
+ "name" : "gnupg-utils",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libcom-err2",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "e2fsprogs",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "libopeniscsiusr",
+ "version" : "2.1.5-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "open-iscsi",
+ "sourceVersion" : "2.1.5-1ubuntu1"
+ },
+ {
+ "name" : "adduser",
+ "version" : "3.118ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.118ubuntu5"
+ },
+ {
+ "name" : "libsasl2-2",
+ "version" : "2.1.27+dfsg2-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cyrus-sasl2",
+ "sourceVersion" : "2.1.27+dfsg2-3ubuntu1"
+ },
+ {
+ "name" : "libglib2.0-data",
+ "version" : "2.72.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glib2.0",
+ "sourceVersion" : "2.72.1-1"
+ },
+ {
+ "name" : "modemmanager",
+ "version" : "1.18.6-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.18.6-1"
+ },
+ {
+ "name" : "python3-more-itertools",
+ "version" : "8.10.0-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "more-itertools",
+ "sourceVersion" : "8.10.0-2"
+ },
+ {
+ "name" : "bc",
+ "version" : "1.7.1-3build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.7.1-3build1"
+ },
+ {
+ "name" : "ed",
+ "version" : "1.18-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.18-1"
+ },
+ {
+ "name" : "augeas-lenses",
+ "version" : "1.13.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "augeas",
+ "sourceVersion" : "1.13.0-1"
+ },
+ {
+ "name" : "libplymouth5",
+ "version" : "0.9.5+git20211018-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "plymouth",
+ "sourceVersion" : "0.9.5+git20211018-1ubuntu3"
+ },
+ {
+ "name" : "fdisk",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "python3-certifi",
+ "version" : "2020.6.20-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-certifi",
+ "sourceVersion" : "2020.6.20-1"
+ },
+ {
+ "name" : "multipath-tools",
+ "version" : "0.8.8-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.8.8-1ubuntu1"
+ },
+ {
+ "name" : "python3-gi",
+ "version" : "3.42.0-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pygobject",
+ "sourceVersion" : "3.42.0-3build1"
+ },
+ {
+ "name" : "keyboard-configuration",
+ "version" : "1.205ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "console-setup",
+ "sourceVersion" : "1.205ubuntu3"
+ },
+ {
+ "name" : "liburcu8",
+ "version" : "0.13.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "liburcu",
+ "sourceVersion" : "0.13.1-1"
+ },
+ {
+ "name" : "libklibc",
+ "version" : "2.0.10-4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "klibc",
+ "sourceVersion" : "2.0.10-4"
+ },
+ {
+ "name" : "python3-json-pointer",
+ "version" : "2.0-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-json-pointer",
+ "sourceVersion" : "2.0-0ubuntu1"
+ },
+ {
+ "name" : "grub-gfxpayload-lists",
+ "version" : "0.7",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.7"
+ },
+ {
+ "name" : "python3-requests",
+ "version" : "2.25.1+dfsg-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "requests",
+ "sourceVersion" : "2.25.1+dfsg-2"
+ },
+ {
+ "name" : "libdrm-common",
+ "version" : "2.4.110-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libdrm",
+ "sourceVersion" : "2.4.110-1ubuntu1"
+ },
+ {
+ "name" : "libp11-kit0",
+ "version" : "0.24.0-6build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "p11-kit",
+ "sourceVersion" : "0.24.0-6build1"
+ },
+ {
+ "name" : "install-info",
+ "version" : "6.8-4build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "texinfo",
+ "sourceVersion" : "6.8-4build1"
+ },
+ {
+ "name" : "liblmdb0",
+ "version" : "0.9.24-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lmdb",
+ "sourceVersion" : "0.9.24-1build2"
+ },
+ {
+ "name" : "linux-image-virtual",
+ "version" : "5.15.0.41.43",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-meta",
+ "sourceVersion" : "5.15.0.41.43"
+ },
+ {
+ "name" : "python3-pyrsistent",
+ "version" : "0.18.1-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyrsistent",
+ "sourceVersion" : "0.18.1-1build1"
+ },
+ {
+ "name" : "snapd",
+ "version" : "2.55.5+22.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.55.5+22.4"
+ },
+ {
+ "name" : "libgudev-1.0-0",
+ "version" : "1:237-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libgudev (237-2build1)",
+ "sourceVersion" : "237-2build1"
+ },
+ {
+ "name" : "grep",
+ "version" : "3.7-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.7-1build1"
+ },
+ {
+ "name" : "fuse3",
+ "version" : "3.10.5-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.10.5-1build1"
+ },
+ {
+ "name" : "libcrypt1",
+ "version" : "1:4.4.27-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxcrypt",
+ "sourceVersion" : "1:4.4.27-1"
+ },
+ {
+ "name" : "libglib2.0-bin",
+ "version" : "2.72.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glib2.0",
+ "sourceVersion" : "2.72.1-1"
+ },
+ {
+ "name" : "libmd0",
+ "version" : "1.0.4-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmd",
+ "sourceVersion" : "1.0.4-1build1"
+ },
+ {
+ "name" : "libnspr4",
+ "version" : "2:4.32-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nspr",
+ "sourceVersion" : "2:4.32-3build1"
+ },
+ {
+ "name" : "libapparmor1",
+ "version" : "3.0.4-2ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apparmor",
+ "sourceVersion" : "3.0.4-2ubuntu2.1"
+ },
+ {
+ "name" : "gzip",
+ "version" : "1.10-4ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.10-4ubuntu4"
+ },
+ {
+ "name" : "libintl-xs-perl",
+ "version" : "1.26-3build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libintl-perl",
+ "sourceVersion" : "1.26-3build2"
+ },
+ {
+ "name" : "libqmi-proxy",
+ "version" : "1.30.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libqmi",
+ "sourceVersion" : "1.30.4-1"
+ },
+ {
+ "name" : "python3-urllib3",
+ "version" : "1.26.5-1~exp1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-urllib3",
+ "sourceVersion" : "1.26.5-1~exp1"
+ },
+ {
+ "name" : "libefiboot1",
+ "version" : "37-6ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "efivar",
+ "sourceVersion" : "37-6ubuntu2"
+ },
+ {
+ "name" : "python3-apt",
+ "version" : "2.3.0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-apt",
+ "sourceVersion" : "2.3.0ubuntu2"
+ },
+ {
+ "name" : "libffi8",
+ "version" : "3.4.2-4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libffi",
+ "sourceVersion" : "3.4.2-4"
+ },
+ {
+ "name" : "pciutils",
+ "version" : "1:3.7.0-6",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:3.7.0-6"
+ },
+ {
+ "name" : "lsb-release",
+ "version" : "11.1.0ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lsb",
+ "sourceVersion" : "11.1.0ubuntu4"
+ },
+ {
+ "name" : "debconf",
+ "version" : "1.5.79ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.5.79ubuntu1"
+ },
+ {
+ "name" : "libfastjson4",
+ "version" : "0.99.9-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libfastjson",
+ "sourceVersion" : "0.99.9-1build2"
+ },
+ {
+ "name" : "libargon2-1",
+ "version" : "0~20171227-0.3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "argon2",
+ "sourceVersion" : "0~20171227-0.3"
+ },
+ {
+ "name" : "lsb-base",
+ "version" : "11.1.0ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lsb",
+ "sourceVersion" : "11.1.0ubuntu4"
+ },
+ {
+ "name" : "libkeyutils1",
+ "version" : "1.6.1-2ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "keyutils",
+ "sourceVersion" : "1.6.1-2ubuntu3"
+ },
+ {
+ "name" : "libnl-genl-3-200",
+ "version" : "3.5.0-0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnl3",
+ "sourceVersion" : "3.5.0-0.1"
+ },
+ {
+ "name" : "gpgsm",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "binutils",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "systemd-timesyncd",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "pinentry-curses",
+ "version" : "1.1.1-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pinentry",
+ "sourceVersion" : "1.1.1-1build2"
+ },
+ {
+ "name" : "python3-ptyprocess",
+ "version" : "0.7.0-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ptyprocess",
+ "sourceVersion" : "0.7.0-3"
+ },
+ {
+ "name" : "apparmor",
+ "version" : "3.0.4-2ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.0.4-2ubuntu2.1"
+ },
+ {
+ "name" : "python3-zipp",
+ "version" : "1.0.0-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-zipp",
+ "sourceVersion" : "1.0.0-3"
+ },
+ {
+ "name" : "libflashrom1",
+ "version" : "1.2-5build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "flashrom",
+ "sourceVersion" : "1.2-5build1"
+ },
+ {
+ "name" : "libdrm2",
+ "version" : "2.4.110-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libdrm",
+ "sourceVersion" : "2.4.110-1ubuntu1"
+ },
+ {
+ "name" : "libcurl3-gnutls",
+ "version" : "7.81.0-1ubuntu1.3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "curl",
+ "sourceVersion" : "7.81.0-1ubuntu1.3"
+ },
+ {
+ "name" : "libcryptsetup12",
+ "version" : "2:2.4.3-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cryptsetup",
+ "sourceVersion" : "2:2.4.3-1ubuntu1"
+ },
+ {
+ "name" : "python3-wadllib",
+ "version" : "1.3.6-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-wadllib",
+ "sourceVersion" : "1.3.6-1"
+ },
+ {
+ "name" : "linux-base",
+ "version" : "4.5ubuntu9",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.5ubuntu9"
+ },
+ {
+ "name" : "linux-headers-5.15.0-41",
+ "version" : "5.15.0-41.44",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux",
+ "sourceVersion" : "5.15.0-41.44"
+ },
+ {
+ "name" : "libnsl2",
+ "version" : "1.3.0-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnsl",
+ "sourceVersion" : "1.3.0-2build2"
+ },
+ {
+ "name" : "python3-oauthlib",
+ "version" : "3.2.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-oauthlib",
+ "sourceVersion" : "3.2.0-1"
+ },
+ {
+ "name" : "libsasl2-modules",
+ "version" : "2.1.27+dfsg2-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cyrus-sasl2",
+ "sourceVersion" : "2.1.27+dfsg2-3ubuntu1"
+ },
+ {
+ "name" : "base-files",
+ "version" : "12ubuntu4.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "12ubuntu4.1"
+ },
+ {
+ "name" : "libtext-charwidth-perl",
+ "version" : "0.4-10build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.4-10build3"
+ },
+ {
+ "name" : "pollinate",
+ "version" : "4.33-3ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.33-3ubuntu2"
+ },
+ {
+ "name" : "landscape-common",
+ "version" : "19.12-0ubuntu13",
+ "publisher" : "Ubuntu",
+ "sourceName" : "landscape-client",
+ "sourceVersion" : "19.12-0ubuntu13"
+ },
+ {
+ "name" : "libisc-export1105",
+ "version" : "1:9.11.19+dfsg-2.1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9-libs",
+ "sourceVersion" : "1:9.11.19+dfsg-2.1ubuntu3"
+ },
+ {
+ "name" : "sudo",
+ "version" : "1.9.9-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.9.9-1ubuntu2"
+ },
+ {
+ "name" : "liblz4-1",
+ "version" : "1.9.3-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lz4",
+ "sourceVersion" : "1.9.3-2build2"
+ },
+ {
+ "name" : "libattr1",
+ "version" : "1:2.5.1-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "attr",
+ "sourceVersion" : "1:2.5.1-1build1"
+ },
+ {
+ "name" : "udisks2",
+ "version" : "2.9.4-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.9.4-1ubuntu2"
+ },
+ {
+ "name" : "ucf",
+ "version" : "3.43",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.43"
+ },
+ {
+ "name" : "libpackagekit-glib2-18",
+ "version" : "1.2.5-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "packagekit",
+ "sourceVersion" : "1.2.5-2ubuntu2"
+ },
+ {
+ "name" : "libxml2",
+ "version" : "2.9.13+dfsg-1ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.9.13+dfsg-1ubuntu0.1"
+ },
+ {
+ "name" : "tcl",
+ "version" : "8.6.11+1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tcltk-defaults",
+ "sourceVersion" : "8.6.11+1build2"
+ },
+ {
+ "name" : "dmidecode",
+ "version" : "3.3-3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.3-3"
+ },
+ {
+ "name" : "usrmerge",
+ "version" : "25ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "25ubuntu2"
+ },
+ {
+ "name" : "libtext-wrapi18n-perl",
+ "version" : "0.6-9",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.6-9"
+ },
+ {
+ "name" : "libelf1",
+ "version" : "0.186-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "elfutils",
+ "sourceVersion" : "0.186-1build1"
+ },
+ {
+ "name" : "dbus-user-session",
+ "version" : "1.12.20-2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "dbus",
+ "sourceVersion" : "1.12.20-2ubuntu4"
+ },
+ {
+ "name" : "lvm2",
+ "version" : "2.3.11-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "ldap-utils",
+ "version" : "2.5.13+dfsg-0ubuntu0.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openldap",
+ "sourceVersion" : "2.5.13+dfsg-0ubuntu0.22.4.1"
+ },
+ {
+ "name" : "python3-click",
+ "version" : "8.0.3-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-click",
+ "sourceVersion" : "8.0.3-1"
+ },
+ {
+ "name" : "python3-babel",
+ "version" : "2.8.0+dfsg.1-7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-babel",
+ "sourceVersion" : "2.8.0+dfsg.1-7"
+ },
+ {
+ "name" : "wget",
+ "version" : "1.21.2-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.21.2-2ubuntu1"
+ },
+ {
+ "name" : "libreadline8",
+ "version" : "8.1.2-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "readline",
+ "sourceVersion" : "8.1.2-1"
+ },
+ {
+ "name" : "libsigsegv2",
+ "version" : "2.13-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsigsegv",
+ "sourceVersion" : "2.13-1ubuntu3"
+ },
+ {
+ "name" : "libxau6",
+ "version" : "1:1.0.9-1build5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxau",
+ "sourceVersion" : "1:1.0.9-1build5"
+ },
+ {
+ "name" : "grub-common",
+ "version" : "2.6-2ubuntu7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "grub2",
+ "sourceVersion" : "2.6-2ubuntu7"
+ },
+ {
+ "name" : "console-setup",
+ "version" : "1.205ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.205ubuntu3"
+ },
+ {
+ "name" : "net-tools",
+ "version" : "1.60+git20181103.0eebece-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.60+git20181103.0eebece-1ubuntu5"
+ },
+ {
+ "name" : "parted",
+ "version" : "3.4-2build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.4-2build1"
+ },
+ {
+ "name" : "libqmi-glib5",
+ "version" : "1.30.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libqmi",
+ "sourceVersion" : "1.30.4-1"
+ },
+ {
+ "name" : "ubuntu-minimal",
+ "version" : "1.481",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-meta",
+ "sourceVersion" : "1.481"
+ },
+ {
+ "name" : "python3-jsonschema",
+ "version" : "3.2.0-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-jsonschema",
+ "sourceVersion" : "3.2.0-0ubuntu2"
+ },
+ {
+ "name" : "powermgmt-base",
+ "version" : "1.36",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.36"
+ },
+ {
+ "name" : "libblockdev-loop2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "init-system-helpers",
+ "version" : "1.62",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.62"
+ },
+ {
+ "name" : "libxmlsec1",
+ "version" : "1.2.33-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xmlsec1",
+ "sourceVersion" : "1.2.33-1build2"
+ },
+ {
+ "name" : "python3-secretstorage",
+ "version" : "3.3.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-secretstorage",
+ "sourceVersion" : "3.3.1-1"
+ },
+ {
+ "name" : "manpages",
+ "version" : "5.10-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.10-1ubuntu1"
+ },
+ {
+ "name" : "gpg-wks-client",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "packagekit",
+ "version" : "1.2.5-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.2.5-2ubuntu2"
+ },
+ {
+ "name" : "sg3-utils-udev",
+ "version" : "1.46-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "sg3-utils",
+ "sourceVersion" : "1.46-1build1"
+ },
+ {
+ "name" : "libssh-4",
+ "version" : "0.9.6-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libssh",
+ "sourceVersion" : "0.9.6-2build1"
+ },
+ {
+ "name" : "python3-chardet",
+ "version" : "4.0.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "chardet",
+ "sourceVersion" : "4.0.0-1"
+ },
+ {
+ "name" : "software-properties-common",
+ "version" : "0.99.22.2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "software-properties",
+ "sourceVersion" : "0.99.22.2"
+ },
+ {
+ "name" : "xauth",
+ "version" : "1:1.1-1build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:1.1-1build2"
+ },
+ {
+ "name" : "libtirpc-common",
+ "version" : "1.3.2-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libtirpc",
+ "sourceVersion" : "1.3.2-2build1"
+ },
+ {
+ "name" : "tpm-udev",
+ "version" : "0.6",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.6"
+ },
+ {
+ "name" : "libpsl5",
+ "version" : "0.21.0-1.2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libpsl",
+ "sourceVersion" : "0.21.0-1.2build2"
+ },
+ {
+ "name" : "libtss2-esys-3.0.2-0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "usb.ids",
+ "version" : "2022.4.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2022.4.2-1"
+ },
+ {
+ "name" : "libmbim-glib4",
+ "version" : "1.26.2-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmbim",
+ "sourceVersion" : "1.26.2-1build1"
+ },
+ {
+ "name" : "packagekit-tools",
+ "version" : "1.2.5-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "packagekit",
+ "sourceVersion" : "1.2.5-2ubuntu2"
+ },
+ {
+ "name" : "ubuntu-advantage-tools",
+ "version" : "27.9~22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "27.9~22.4.1"
+ },
+ {
+ "name" : "htop",
+ "version" : "3.0.5-7build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.0.5-7build2"
+ },
+ {
+ "name" : "pwgen",
+ "version" : "2.8-2build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.8-2build1"
+ },
+ {
+ "name" : "e2fsprogs",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "libgmp10",
+ "version" : "2:6.2.1+dfsg-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gmp",
+ "sourceVersion" : "2:6.2.1+dfsg-3ubuntu1"
+ },
+ {
+ "name" : "libicu70",
+ "version" : "70.1-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "icu",
+ "sourceVersion" : "70.1-2"
+ },
+ {
+ "name" : "python3-jinja2",
+ "version" : "3.0.3-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "jinja2",
+ "sourceVersion" : "3.0.3-1"
+ },
+ {
+ "name" : "systemd",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "bolt",
+ "version" : "0.9.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.9.2-1"
+ },
+ {
+ "name" : "iputils-ping",
+ "version" : "3:20211215-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iputils",
+ "sourceVersion" : "3:20211215-1"
+ },
+ {
+ "name" : "libudisks2-0",
+ "version" : "2.9.4-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "udisks2",
+ "sourceVersion" : "2.9.4-1ubuntu2"
+ },
+ {
+ "name" : "ubuntu-server",
+ "version" : "1.481",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-meta",
+ "sourceVersion" : "1.481"
+ },
+ {
+ "name" : "python-babel-localedata",
+ "version" : "2.8.0+dfsg.1-7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-babel",
+ "sourceVersion" : "2.8.0+dfsg.1-7"
+ },
+ {
+ "name" : "libsort-naturally-perl",
+ "version" : "1.3-2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.3-2"
+ },
+ {
+ "name" : "libxmlsec1-openssl",
+ "version" : "1.2.33-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xmlsec1",
+ "sourceVersion" : "1.2.33-1build2"
+ },
+ {
+ "name" : "python3.10-minimal",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3.10",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "psmisc",
+ "version" : "23.4-2build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "23.4-2build3"
+ },
+ {
+ "name" : "base-passwd",
+ "version" : "3.5.52build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.5.52build1"
+ },
+ {
+ "name" : "libaudit1",
+ "version" : "1:3.0.7-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "audit",
+ "sourceVersion" : "1:3.0.7-1build1"
+ },
+ {
+ "name" : "dbus",
+ "version" : "1.12.20-2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.12.20-2ubuntu4"
+ },
+ {
+ "name" : "libjq1",
+ "version" : "1.6-2.1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "jq",
+ "sourceVersion" : "1.6-2.1ubuntu3"
+ },
+ {
+ "name" : "squashfs-tools",
+ "version" : "1:4.5-3build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:4.5-3build1"
+ },
+ {
+ "name" : "libmagic-mgc",
+ "version" : "1:5.41-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "file",
+ "sourceVersion" : "1:5.41-3"
+ },
+ {
+ "name" : "libip6tc2",
+ "version" : "1.8.7-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iptables",
+ "sourceVersion" : "1.8.7-1ubuntu5"
+ },
+ {
+ "name" : "libctf-nobfd0",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "python3-yaml",
+ "version" : "5.4.1-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyyaml",
+ "sourceVersion" : "5.4.1-1ubuntu1"
+ },
+ {
+ "name" : "gpg-wks-server",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libnuma1",
+ "version" : "2.0.14-3ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "numactl",
+ "sourceVersion" : "2.0.14-3ubuntu2"
+ },
+ {
+ "name" : "zsh-common",
+ "version" : "5.8.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "zsh",
+ "sourceVersion" : "5.8.1-1"
+ },
+ {
+ "name" : "dmsetup",
+ "version" : "2:1.2.175-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2 (2.03.11-2.1ubuntu4)",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "binutils-common",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "libtss2-tcti-device0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "sg3-utils",
+ "version" : "1.46-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.46-1build1"
+ },
+ {
+ "name" : "libkmod2",
+ "version" : "29-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "kmod",
+ "sourceVersion" : "29-1ubuntu1"
+ },
+ {
+ "name" : "python3-serial",
+ "version" : "3.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyserial",
+ "sourceVersion" : "3.5-1"
+ },
+ {
+ "name" : "libwrap0",
+ "version" : "7.6.q-31build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tcp-wrappers",
+ "sourceVersion" : "7.6.q-31build2"
+ },
+ {
+ "name" : "open-iscsi",
+ "version" : "2.1.5-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.1.5-1ubuntu1"
+ },
+ {
+ "name" : "libpython3.10-stdlib",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3.10",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "python3-debconf",
+ "version" : "1.5.79ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "debconf",
+ "sourceVersion" : "1.5.79ubuntu1"
+ },
+ {
+ "name" : "bsdutils",
+ "version" : "1:2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux (2.37.2-4ubuntu3)",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "zerofree",
+ "version" : "1.1.1-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.1.1-1build3"
+ },
+ {
+ "name" : "eatmydata",
+ "version" : "130-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libeatmydata",
+ "sourceVersion" : "130-2build1"
+ },
+ {
+ "name" : "libjcat1",
+ "version" : "0.1.9-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libjcat",
+ "sourceVersion" : "0.1.9-1"
+ },
+ {
+ "name" : "linux-headers-virtual",
+ "version" : "5.15.0.41.43",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-meta",
+ "sourceVersion" : "5.15.0.41.43"
+ },
+ {
+ "name" : "python3-lazr.uri",
+ "version" : "1.0.6-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lazr.uri",
+ "sourceVersion" : "1.0.6-2"
+ },
+ {
+ "name" : "libldap-common",
+ "version" : "2.5.12+dfsg-0ubuntu0.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openldap",
+ "sourceVersion" : "2.5.12+dfsg-0ubuntu0.22.4.1"
+ },
+ {
+ "name" : "policykit-1",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "kbd",
+ "version" : "2.3.0-3ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.3.0-3ubuntu4"
+ },
+ {
+ "name" : "byobu",
+ "version" : "5.133-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.133-1"
+ },
+ {
+ "name" : "procps",
+ "version" : "2:3.3.17-6ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2:3.3.17-6ubuntu2"
+ },
+ {
+ "name" : "xdg-user-dirs",
+ "version" : "0.17-2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.17-2ubuntu4"
+ },
+ {
+ "name" : "libfuse3-3",
+ "version" : "3.10.5-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fuse3",
+ "sourceVersion" : "3.10.5-1build1"
+ },
+ {
+ "name" : "dmeventd",
+ "version" : "2:1.2.175-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2 (2.03.11-2.1ubuntu4)",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "libgpm2",
+ "version" : "1.20.7-10build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gpm",
+ "sourceVersion" : "1.20.7-10build1"
+ },
+ {
+ "name" : "libdbus-1-3",
+ "version" : "1.12.20-2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "dbus",
+ "sourceVersion" : "1.12.20-2ubuntu4"
+ },
+ {
+ "name" : "python3-hamcrest",
+ "version" : "2.0.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyhamcrest",
+ "sourceVersion" : "2.0.2-2"
+ },
+ {
+ "name" : "update-notifier-common",
+ "version" : "3.192.54",
+ "publisher" : "Ubuntu",
+ "sourceName" : "update-notifier",
+ "sourceVersion" : "3.192.54"
+ },
+ {
+ "name" : "libevent-core-2.1-7",
+ "version" : "2.1.12-stable-1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libevent",
+ "sourceVersion" : "2.1.12-stable-1build3"
+ },
+ {
+ "name" : "sysvinit-utils",
+ "version" : "3.1-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "sysvinit",
+ "sourceVersion" : "3.1-1ubuntu1"
+ },
+ {
+ "name" : "python3-lazr.restfulclient",
+ "version" : "0.14.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lazr.restfulclient",
+ "sourceVersion" : "0.14.4-1"
+ },
+ {
+ "name" : "debconf-i18n",
+ "version" : "1.5.79ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "debconf",
+ "sourceVersion" : "1.5.79ubuntu1"
+ },
+ {
+ "name" : "libxxhash0",
+ "version" : "0.8.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xxhash",
+ "sourceVersion" : "0.8.1-1"
+ },
+ {
+ "name" : "unattended-upgrades",
+ "version" : "2.8ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.8ubuntu1"
+ },
+ {
+ "name" : "libjson-glib-1.0-common",
+ "version" : "1.6.6-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "json-glib",
+ "sourceVersion" : "1.6.6-1build1"
+ },
+ {
+ "name" : "ncurses-term",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libpam-systemd",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "liblzo2-2",
+ "version" : "2.10-2build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lzo2",
+ "sourceVersion" : "2.10-2build3"
+ },
+ {
+ "name" : "libnetplan0",
+ "version" : "0.104-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "netplan.io",
+ "sourceVersion" : "0.104-0ubuntu2"
+ },
+ {
+ "name" : "cloud-initramfs-dyn-netconf",
+ "version" : "0.47ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cloud-initramfs-tools",
+ "sourceVersion" : "0.47ubuntu1"
+ },
+ {
+ "name" : "libuv1",
+ "version" : "1.43.0-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.43.0-1"
+ },
+ {
+ "name" : "ethtool",
+ "version" : "1:5.16-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:5.16-1"
+ },
+ {
+ "name" : "cron",
+ "version" : "3.0pl1-137ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.0pl1-137ubuntu3"
+ },
+ {
+ "name" : "libnftnl11",
+ "version" : "1.2.1-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnftnl",
+ "sourceVersion" : "1.2.1-1build1"
+ },
+ {
+ "name" : "libgcrypt20",
+ "version" : "1.9.4-3ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.9.4-3ubuntu3"
+ },
+ {
+ "name" : "libntfs-3g89",
+ "version" : "1:2021.8.22-3ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ntfs-3g",
+ "sourceVersion" : "1:2021.8.22-3ubuntu1.1"
+ },
+ {
+ "name" : "usb-modeswitch",
+ "version" : "2.6.1-3ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.6.1-3ubuntu2"
+ },
+ {
+ "name" : "distro-info",
+ "version" : "1.1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.1build1"
+ },
+ {
+ "name" : "libvolume-key1",
+ "version" : "0.3.12-3.1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "volume-key",
+ "sourceVersion" : "0.3.12-3.1build3"
+ },
+ {
+ "name" : "libksba8",
+ "version" : "1.6.0-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libksba",
+ "sourceVersion" : "1.6.0-2build1"
+ },
+ {
+ "name" : "libnetfilter-conntrack3",
+ "version" : "1.0.9-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnetfilter-conntrack",
+ "sourceVersion" : "1.0.9-1"
+ },
+ {
+ "name" : "shared-mime-info",
+ "version" : "2.1-2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.1-2"
+ },
+ {
+ "name" : "distro-info-data",
+ "version" : "0.52ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.52ubuntu0.1"
+ },
+ {
+ "name" : "libtss2-tcti-swtpm0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "libaudit-common",
+ "version" : "1:3.0.7-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "audit",
+ "sourceVersion" : "1:3.0.7-1build1"
+ },
+ {
+ "name" : "libtcl8.6",
+ "version" : "8.6.12+dfsg-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tcl8.6",
+ "sourceVersion" : "8.6.12+dfsg-1build1"
+ },
+ {
+ "name" : "overlayroot",
+ "version" : "0.47ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cloud-initramfs-tools",
+ "sourceVersion" : "0.47ubuntu1"
+ },
+ {
+ "name" : "python3-keyring",
+ "version" : "23.5.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-keyring",
+ "sourceVersion" : "23.5.0-1"
+ },
+ {
+ "name" : "irqbalance",
+ "version" : "1.8.0-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.8.0-1build1"
+ },
+ {
+ "name" : "xfsprogs",
+ "version" : "5.13.0-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.13.0-1ubuntu2"
+ },
+ {
+ "name" : "libunistring2",
+ "version" : "1.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libunistring",
+ "sourceVersion" : "1.0-1"
+ },
+ {
+ "name" : "libx11-data",
+ "version" : "2:1.7.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libx11",
+ "sourceVersion" : "2:1.7.5-1"
+ },
+ {
+ "name" : "libpam-cap",
+ "version" : "1:2.44-1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libcap2",
+ "sourceVersion" : "1:2.44-1build3"
+ },
+ {
+ "name" : "initramfs-tools",
+ "version" : "0.140ubuntu13",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.140ubuntu13"
+ },
+ {
+ "name" : "hdparm",
+ "version" : "9.60+ds-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "9.60+ds-1build3"
+ },
+ {
+ "name" : "isc-dhcp-common",
+ "version" : "4.4.1-2.3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "isc-dhcp",
+ "sourceVersion" : "4.4.1-2.3ubuntu2.1"
+ },
+ {
+ "name" : "libparted-fs-resize0",
+ "version" : "3.4-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "parted",
+ "sourceVersion" : "3.4-2build1"
+ },
+ {
+ "name" : "bind9-dnsutils",
+ "version" : "1:9.18.1-1ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9",
+ "sourceVersion" : "1:9.18.1-1ubuntu1.1"
+ },
+ {
+ "name" : "libxext6",
+ "version" : "2:1.3.4-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxext",
+ "sourceVersion" : "2:1.3.4-1build1"
+ },
+ {
+ "name" : "liblocale-gettext-perl",
+ "version" : "1.7-4build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.7-4build3"
+ },
+ {
+ "name" : "librtmp1",
+ "version" : "2.4+20151223.gitfa8646d.1-2build4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "rtmpdump",
+ "sourceVersion" : "2.4+20151223.gitfa8646d.1-2build4"
+ },
+ {
+ "name" : "secureboot-db",
+ "version" : "1.8",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.8"
+ },
+ {
+ "name" : "libcbor0.8",
+ "version" : "0.8.0-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libcbor",
+ "sourceVersion" : "0.8.0-2ubuntu1"
+ },
+ {
+ "name" : "python3-problem-report",
+ "version" : "2.20.11-0ubuntu82.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apport",
+ "sourceVersion" : "2.20.11-0ubuntu82.1"
+ },
+ {
+ "name" : "iptables",
+ "version" : "1.8.7-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.8.7-1ubuntu5"
+ },
+ {
+ "name" : "rsync",
+ "version" : "3.2.3-8ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.2.3-8ubuntu3"
+ },
+ {
+ "name" : "klibc-utils",
+ "version" : "2.0.10-4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "klibc",
+ "sourceVersion" : "2.0.10-4"
+ },
+ {
+ "name" : "nftables",
+ "version" : "1.0.2-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.0.2-1ubuntu2"
+ },
+ {
+ "name" : "zlib1g",
+ "version" : "1:1.2.11.dfsg-2ubuntu9",
+ "publisher" : "Ubuntu",
+ "sourceName" : "zlib",
+ "sourceVersion" : "1:1.2.11.dfsg-2ubuntu9"
+ },
+ {
+ "name" : "vim",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "libip4tc2",
+ "version" : "1.8.7-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iptables",
+ "sourceVersion" : "1.8.7-1ubuntu5"
+ },
+ {
+ "name" : "libonig5",
+ "version" : "6.9.7.1-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libonig",
+ "sourceVersion" : "6.9.7.1-2build1"
+ },
+ {
+ "name" : "grub2-common",
+ "version" : "2.6-2ubuntu7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "grub2",
+ "sourceVersion" : "2.6-2ubuntu7"
+ },
+ {
+ "name" : "command-not-found",
+ "version" : "22.4.0",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "22.4.0"
+ },
+ {
+ "name" : "python3-importlib-metadata",
+ "version" : "4.6.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-importlib-metadata",
+ "sourceVersion" : "4.6.4-1"
+ },
+ {
+ "name" : "liblzma5",
+ "version" : "5.2.5-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xz-utils",
+ "sourceVersion" : "5.2.5-2ubuntu1"
+ },
+ {
+ "name" : "libperl5.34",
+ "version" : "5.34.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "perl",
+ "sourceVersion" : "5.34.0-3ubuntu1"
+ },
+ {
+ "name" : "dirmngr",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libparted2",
+ "version" : "3.4-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "parted",
+ "sourceVersion" : "3.4-2build1"
+ },
+ {
+ "name" : "cryptsetup-initramfs",
+ "version" : "2:2.4.3-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cryptsetup",
+ "sourceVersion" : "2:2.4.3-1ubuntu1"
+ },
+ {
+ "name" : "tzdata",
+ "version" : "2022a-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2022a-0ubuntu1"
+ },
+ {
+ "name" : "ca-certificates",
+ "version" : "20211016",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "20211016"
+ },
+ {
+ "name" : "python3-configobj",
+ "version" : "5.0.6-5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "configobj",
+ "sourceVersion" : "5.0.6-5"
+ },
+ {
+ "name" : "libzstd1",
+ "version" : "1.4.8+dfsg-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libzstd",
+ "sourceVersion" : "1.4.8+dfsg-3build1"
+ },
+ {
+ "name" : "libblkid1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libctf0",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "coreutils",
+ "version" : "8.32-4.1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "8.32-4.1ubuntu1"
+ },
+ {
+ "name" : "xxd",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "vim",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "apt-utils",
+ "version" : "2.4.5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apt",
+ "sourceVersion" : "2.4.5"
+ },
+ {
+ "name" : "strace",
+ "version" : "5.16-0ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.16-0ubuntu3"
+ },
+ {
+ "name" : "libnpth0",
+ "version" : "1.6-3build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "npth",
+ "sourceVersion" : "1.6-3build2"
+ },
+ {
+ "name" : "libxmlb2",
+ "version" : "0.3.6-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxmlb",
+ "sourceVersion" : "0.3.6-2build1"
+ },
+ {
+ "name" : "python3-pyasn1",
+ "version" : "0.4.8-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyasn1",
+ "sourceVersion" : "0.4.8-1"
+ },
+ {
+ "name" : "libnl-3-200",
+ "version" : "3.5.0-0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnl3",
+ "sourceVersion" : "3.5.0-0.1"
+ },
+ {
+ "name" : "libtasn1-6",
+ "version" : "4.18.0-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.18.0-4build1"
+ },
+ {
+ "name" : "busybox-initramfs",
+ "version" : "1:1.30.1-7ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "busybox",
+ "sourceVersion" : "1:1.30.1-7ubuntu3"
+ },
+ {
+ "name" : "dpkg",
+ "version" : "1.21.1ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.21.1ubuntu2.1"
+ },
+ {
+ "name" : "ubuntu-standard",
+ "version" : "1.481",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-meta",
+ "sourceVersion" : "1.481"
+ },
+ {
+ "name" : "libgusb2",
+ "version" : "0.3.10-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libgusb",
+ "sourceVersion" : "0.3.10-1"
+ },
+ {
+ "name" : "libpython3-stdlib",
+ "version" : "3.10.4-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-defaults",
+ "sourceVersion" : "3.10.4-0ubuntu2"
+ },
+ {
+ "name" : "libblockdev-swap2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "cloud-init",
+ "version" : "22.2-0ubuntu1~22.4.3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "22.2-0ubuntu1~22.4.3"
+ },
+ {
+ "name" : "libexpat1",
+ "version" : "2.4.7-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "expat",
+ "sourceVersion" : "2.4.7-1"
+ },
+ {
+ "name" : "openssh-sftp-server",
+ "version" : "1:8.9p1-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openssh",
+ "sourceVersion" : "1:8.9p1-3"
+ },
+ {
+ "name" : "libpam-modules-bin",
+ "version" : "1.4.0-11ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pam",
+ "sourceVersion" : "1.4.0-11ubuntu2"
+ },
+ {
+ "name" : "linux-headers-5.15.0-41-generic",
+ "version" : "5.15.0-41.44",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux",
+ "sourceVersion" : "5.15.0-41.44"
+ },
+ {
+ "name" : "tcpdump",
+ "version" : "4.99.1-3build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.99.1-3build2"
+ },
+ {
+ "name" : "logsave",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "e2fsprogs",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "linux-virtual",
+ "version" : "5.15.0.41.43",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-meta",
+ "sourceVersion" : "5.15.0.41.43"
+ },
+ {
+ "name" : "python3-gdbm",
+ "version" : "3.10.4-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-stdlib-extensions",
+ "sourceVersion" : "3.10.4-0ubuntu1"
+ },
+ {
+ "name" : "python3-markupsafe",
+ "version" : "2.0.1-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "markupsafe",
+ "sourceVersion" : "2.0.1-2build1"
+ },
+ {
+ "name" : "libgssapi-krb5-2",
+ "version" : "1.19.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "krb5",
+ "sourceVersion" : "1.19.2-2"
+ },
+ {
+ "name" : "ssh-import-id",
+ "version" : "5.11-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.11-0ubuntu1"
+ },
+ {
+ "name" : "screen",
+ "version" : "4.9.0-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.9.0-1"
+ },
+ {
+ "name" : "logrotate",
+ "version" : "3.19.0-1ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.19.0-1ubuntu1.1"
+ },
+ {
+ "name" : "libfido2-1",
+ "version" : "1.10.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libfido2",
+ "sourceVersion" : "1.10.0-1"
+ },
+ {
+ "name" : "debianutils",
+ "version" : "5.5-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.5-1ubuntu2"
+ },
+ {
+ "name" : "perl-modules-5.34",
+ "version" : "5.34.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "perl",
+ "sourceVersion" : "5.34.0-3ubuntu1"
+ },
+ {
+ "name" : "libpam0g",
+ "version" : "1.4.0-11ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pam",
+ "sourceVersion" : "1.4.0-11ubuntu2"
+ },
+ {
+ "name" : "netbase",
+ "version" : "6.3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "6.3"
+ },
+ {
+ "name" : "python3-dbus",
+ "version" : "1.2.18-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "dbus-python",
+ "sourceVersion" : "1.2.18-3build1"
+ },
+ {
+ "name" : "augeas-tools",
+ "version" : "1.13.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "augeas",
+ "sourceVersion" : "1.13.0-1"
+ },
+ {
+ "name" : "pkexec",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceName" : "policykit-1",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "plymouth-theme-ubuntu-text",
+ "version" : "0.9.5+git20211018-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "plymouth",
+ "sourceVersion" : "0.9.5+git20211018-1ubuntu3"
+ },
+ {
+ "name" : "polkitd",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceName" : "policykit-1",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "libxcb1",
+ "version" : "1.14-3ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxcb",
+ "sourceVersion" : "1.14-3ubuntu3"
+ },
+ {
+ "name" : "libpam-runtime",
+ "version" : "1.4.0-11ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pam",
+ "sourceVersion" : "1.4.0-11ubuntu2"
+ },
+ {
+ "name" : "xz-utils",
+ "version" : "5.2.5-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.2.5-2ubuntu1"
+ },
+ {
+ "name" : "libgirepository-1.0-1",
+ "version" : "1.72.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gobject-introspection",
+ "sourceVersion" : "1.72.0-1"
+ },
+ {
+ "name" : "libselinux1",
+ "version" : "3.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libselinux",
+ "sourceVersion" : "3.3-1build2"
+ },
+ {
+ "name" : "cpio",
+ "version" : "2.13+dfsg-7",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.13+dfsg-7"
+ },
+ {
+ "name" : "libpython3.10",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3.10",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "libdevmapper-event1.02.1",
+ "version" : "2:1.2.175-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2 (2.03.11-2.1ubuntu4)",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "python3-pkg-resources",
+ "version" : "59.6.0-1.2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "setuptools",
+ "sourceVersion" : "59.6.0-1.2"
+ },
+ {
+ "name" : "mtr-tiny",
+ "version" : "0.95-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "mtr",
+ "sourceVersion" : "0.95-1"
+ },
+ {
+ "name" : "libterm-readkey-perl",
+ "version" : "2.38-1build4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.38-1build4"
+ },
+ {
+ "name" : "bcache-tools",
+ "version" : "1.0.8-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.0.8-4ubuntu3"
+ },
+ {
+ "name" : "initramfs-tools-bin",
+ "version" : "0.140ubuntu13",
+ "publisher" : "Ubuntu",
+ "sourceName" : "initramfs-tools",
+ "sourceVersion" : "0.140ubuntu13"
+ },
+ {
+ "name" : "libsgutils2-2",
+ "version" : "1.46-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "sg3-utils",
+ "sourceVersion" : "1.46-1build1"
+ },
+ {
+ "name" : "libmodule-find-perl",
+ "version" : "0.15-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.15-1"
+ },
+ {
+ "name" : "isc-dhcp-client",
+ "version" : "4.4.1-2.3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "isc-dhcp",
+ "sourceVersion" : "4.4.1-2.3ubuntu2.1"
+ },
+ {
+ "name" : "libaio1",
+ "version" : "0.3.112-13build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libaio",
+ "sourceVersion" : "0.3.112-13build1"
+ },
+ {
+ "name" : "libgnutls30",
+ "version" : "3.7.3-4ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnutls28",
+ "sourceVersion" : "3.7.3-4ubuntu1"
+ },
+ {
+ "name" : "libsmartcols1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "openssl",
+ "version" : "3.0.2-0ubuntu1.6",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.0.2-0ubuntu1.6"
+ },
+ {
+ "name" : "plymouth",
+ "version" : "0.9.5+git20211018-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.9.5+git20211018-1ubuntu3"
+ },
+ {
+ "name" : "libcap-ng0",
+ "version" : "0.7.9-2.2build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libcap-ng",
+ "sourceVersion" : "0.7.9-2.2build3"
+ },
+ {
+ "name" : "python3-debian",
+ "version" : "0.1.43ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-debian",
+ "sourceVersion" : "0.1.43ubuntu1"
+ },
+ {
+ "name" : "thin-provisioning-tools",
+ "version" : "0.9.0-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.9.0-2ubuntu1"
+ },
+ {
+ "name" : "apport-symptoms",
+ "version" : "0.24",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.24"
+ },
+ {
+ "name" : "liblvm2cmd2.03",
+ "version" : "2.3.11-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "libdw1",
+ "version" : "0.186-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "elfutils",
+ "sourceVersion" : "0.186-1build1"
+ },
+ {
+ "name" : "gir1.2-glib-2.0",
+ "version" : "1.72.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gobject-introspection",
+ "sourceVersion" : "1.72.0-1"
+ },
+ {
+ "name" : "libtss2-sys1",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "python3-distutils",
+ "version" : "3.10.4-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-stdlib-extensions",
+ "sourceVersion" : "3.10.4-0ubuntu1"
+ },
+ {
+ "name" : "curl",
+ "version" : "7.81.0-1ubuntu1.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "7.81.0-1ubuntu1.4"
+ },
+ {
+ "name" : "mount",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libnettle8",
+ "version" : "3.7.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nettle",
+ "sourceVersion" : "3.7.3-1build2"
+ },
+ {
+ "name" : "linux-headers-generic",
+ "version" : "5.15.0.41.43",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-meta",
+ "sourceVersion" : "5.15.0.41.43"
+ },
+ {
+ "name" : "libprocps8",
+ "version" : "2:3.3.17-6ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "procps",
+ "sourceVersion" : "2:3.3.17-6ubuntu2"
+ },
+ {
+ "name" : "libintl-perl",
+ "version" : "1.26-3build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.26-3build2"
+ },
+ {
+ "name" : "libgdbm6",
+ "version" : "1.23-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gdbm",
+ "sourceVersion" : "1.23-1"
+ },
+ {
+ "name" : "python3-setuptools",
+ "version" : "59.6.0-1.2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "setuptools",
+ "sourceVersion" : "59.6.0-1.2"
+ },
+ {
+ "name" : "tree",
+ "version" : "2.0.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.0.2-1"
+ },
+ {
+ "name" : "libnewt0.52",
+ "version" : "0.52.21-5ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "newt",
+ "sourceVersion" : "0.52.21-5ubuntu2"
+ },
+ {
+ "name" : "python3-colorama",
+ "version" : "0.4.4-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-colorama",
+ "sourceVersion" : "0.4.4-1"
+ },
+ {
+ "name" : "dash",
+ "version" : "0.5.11+git20210903+57cd650a4ed-3build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.5.11+git20210903+57cd650a4ed-3build1"
+ },
+ {
+ "name" : "libfwupdplugin5",
+ "version" : "1.7.5-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fwupd",
+ "sourceVersion" : "1.7.5-3"
+ },
+ {
+ "name" : "libfribidi0",
+ "version" : "1.0.8-2ubuntu3.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fribidi",
+ "sourceVersion" : "1.0.8-2ubuntu3.1"
+ },
+ {
+ "name" : "libxslt1.1",
+ "version" : "1.1.34-4build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxslt",
+ "sourceVersion" : "1.1.34-4build2"
+ },
+ {
+ "name" : "binutils-x86-64-linux-gnu",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "libltdl7",
+ "version" : "2.4.6-15build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libtool",
+ "sourceVersion" : "2.4.6-15build2"
+ },
+ {
+ "name" : "libsasl2-modules-db",
+ "version" : "2.1.27+dfsg2-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cyrus-sasl2",
+ "sourceVersion" : "2.1.27+dfsg2-3ubuntu1"
+ },
+ {
+ "name" : "locales",
+ "version" : "2.35-0ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glibc",
+ "sourceVersion" : "2.35-0ubuntu3"
+ },
+ {
+ "name" : "libaugeas0",
+ "version" : "1.13.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "augeas",
+ "sourceVersion" : "1.13.0-1"
+ },
+ {
+ "name" : "libmodule-scandeps-perl",
+ "version" : "1.31-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.31-1"
+ },
+ {
+ "name" : "sosreport",
+ "version" : "4.3-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.3-1ubuntu2"
+ },
+ {
+ "name" : "python-apt-common",
+ "version" : "2.3.0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-apt",
+ "sourceVersion" : "2.3.0ubuntu2"
+ },
+ {
+ "name" : "libslang2",
+ "version" : "2.3.2-5build4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "slang2",
+ "sourceVersion" : "2.3.2-5build4"
+ },
+ {
+ "name" : "zstd",
+ "version" : "1.4.8+dfsg-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libzstd",
+ "sourceVersion" : "1.4.8+dfsg-3build1"
+ },
+ {
+ "name" : "libdb5.3",
+ "version" : "5.3.28+dfsg1-0.8ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "db5.3",
+ "sourceVersion" : "5.3.28+dfsg1-0.8ubuntu3"
+ },
+ {
+ "name" : "cloud-guest-utils",
+ "version" : "0.32-22-g45fe84a5-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cloud-utils",
+ "sourceVersion" : "0.32-22-g45fe84a5-0ubuntu1"
+ },
+ {
+ "name" : "libatm1",
+ "version" : "1:2.5.1-4build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-atm",
+ "sourceVersion" : "1:2.5.1-4build2"
+ },
+ {
+ "name" : "libext2fs2",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "e2fsprogs",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "python3-distro",
+ "version" : "1.7.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-distro",
+ "sourceVersion" : "1.7.0-1"
+ },
+ {
+ "name" : "eject",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libacl1",
+ "version" : "2.3.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "acl",
+ "sourceVersion" : "2.3.1-1"
+ },
+ {
+ "name" : "libnftables1",
+ "version" : "1.0.2-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nftables",
+ "sourceVersion" : "1.0.2-1ubuntu2"
+ },
+ {
+ "name" : "apt",
+ "version" : "2.4.5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.4.5"
+ },
+ {
+ "name" : "apport",
+ "version" : "2.20.11-0ubuntu82.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.20.11-0ubuntu82.1"
+ },
+ {
+ "name" : "kmod",
+ "version" : "29-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "29-1ubuntu1"
+ },
+ {
+ "name" : "xkb-data",
+ "version" : "2.33-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "xkeyboard-config",
+ "sourceVersion" : "2.33-1"
+ },
+ {
+ "name" : "python3-pyparsing",
+ "version" : "2.4.7-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyparsing",
+ "sourceVersion" : "2.4.7-1"
+ },
+ {
+ "name" : "needrestart",
+ "version" : "3.5-5ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.5-5ubuntu2.1"
+ },
+ {
+ "name" : "libcap2",
+ "version" : "1:2.44-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:2.44-1build3"
+ },
+ {
+ "name" : "perl-base",
+ "version" : "5.34.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "perl",
+ "sourceVersion" : "5.34.0-3ubuntu1"
+ },
+ {
+ "name" : "rudder-agent",
+ "version" : "7.2.0~rc1-ubuntu22.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "7.2.0~rc1-ubuntu22.4"
+ },
+ {
+ "name" : "libgcab-1.0-0",
+ "version" : "1.4-3build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gcab",
+ "sourceVersion" : "1.4-3build2"
+ },
+ {
+ "name" : "motd-news-config",
+ "version" : "12ubuntu4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "base-files",
+ "sourceVersion" : "12ubuntu4.1"
+ },
+ {
+ "name" : "libfdisk1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libxtables12",
+ "version" : "1.8.7-1ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iptables",
+ "sourceVersion" : "1.8.7-1ubuntu5"
+ },
+ {
+ "name" : "tmux",
+ "version" : "3.2a-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.2a-4build1"
+ },
+ {
+ "name" : "gpgconf",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "iproute2",
+ "version" : "5.15.0-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.15.0-1ubuntu2"
+ },
+ {
+ "name" : "gpg",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "python3-jeepney",
+ "version" : "0.7.1-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "jeepney",
+ "sourceVersion" : "0.7.1-3"
+ },
+ {
+ "name" : "libpcre2-8-0",
+ "version" : "10.39-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pcre2",
+ "sourceVersion" : "10.39-3build1"
+ },
+ {
+ "name" : "uuid-runtime",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "openssh-server",
+ "version" : "1:8.9p1-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openssh",
+ "sourceVersion" : "1:8.9p1-3"
+ },
+ {
+ "name" : "ftp",
+ "version" : "20210827-4build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tnftp",
+ "sourceVersion" : "20210827-4build1"
+ },
+ {
+ "name" : "libestr0",
+ "version" : "0.1.10-2.1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libestr",
+ "sourceVersion" : "0.1.10-2.1build3"
+ },
+ {
+ "name" : "python3-pexpect",
+ "version" : "4.8.0-2ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pexpect",
+ "sourceVersion" : "4.8.0-2ubuntu1"
+ },
+ {
+ "name" : "time",
+ "version" : "1.9-0.1build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.9-0.1build2"
+ },
+ {
+ "name" : "python3-netifaces",
+ "version" : "0.11.0-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "netifaces",
+ "sourceVersion" : "0.11.0-1build2"
+ },
+ {
+ "name" : "libblockdev2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "rsyslog",
+ "version" : "8.2112.0-2ubuntu2.2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "8.2112.0-2ubuntu2.2"
+ },
+ {
+ "name" : "libblockdev-crypto2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "libkrb5support0",
+ "version" : "1.19.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "krb5",
+ "sourceVersion" : "1.19.2-2"
+ },
+ {
+ "name" : "libxml-treepp-perl",
+ "version" : "0.43-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.43-1"
+ },
+ {
+ "name" : "libnfnetlink0",
+ "version" : "1.0.1-3build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libnfnetlink",
+ "sourceVersion" : "1.0.1-3build3"
+ },
+ {
+ "name" : "iputils-tracepath",
+ "version" : "3:20211215-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "iputils",
+ "sourceVersion" : "3:20211215-1"
+ },
+ {
+ "name" : "bsdextrautils",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libnss3",
+ "version" : "2:3.68.2-0ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nss",
+ "sourceVersion" : "2:3.68.2-0ubuntu1.1"
+ },
+ {
+ "name" : "libglib2.0-0",
+ "version" : "2.72.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glib2.0",
+ "sourceVersion" : "2.72.1-1"
+ },
+ {
+ "name" : "libgpg-error0",
+ "version" : "1.43-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libgpg-error",
+ "sourceVersion" : "1.43-3"
+ },
+ {
+ "name" : "python3-newt",
+ "version" : "0.52.21-5ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "newt",
+ "sourceVersion" : "0.52.21-5ubuntu2"
+ },
+ {
+ "name" : "libpython3.10-minimal",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3.10",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "less",
+ "version" : "590-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "590-1build1"
+ },
+ {
+ "name" : "gawk",
+ "version" : "1:5.1.0-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:5.1.0-1build3"
+ },
+ {
+ "name" : "libc6",
+ "version" : "2.35-0ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glibc",
+ "sourceVersion" : "2.35-0ubuntu3"
+ },
+ {
+ "name" : "apt-transport-https",
+ "version" : "2.4.7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apt",
+ "sourceVersion" : "2.4.7"
+ },
+ {
+ "name" : "liberror-perl",
+ "version" : "0.17029-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.17029-1"
+ },
+ {
+ "name" : "libseccomp2",
+ "version" : "2.5.3-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libseccomp",
+ "sourceVersion" : "2.5.3-2ubuntu2"
+ },
+ {
+ "name" : "libmount1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "netcat-openbsd",
+ "version" : "1.218-4ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.218-4ubuntu1"
+ },
+ {
+ "name" : "systemd-sysv",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "libblockdev-utils2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "git",
+ "version" : "1:2.34.1-1ubuntu1.4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:2.34.1-1ubuntu1.4"
+ },
+ {
+ "name" : "busybox-static",
+ "version" : "1:1.30.1-7ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "busybox",
+ "sourceVersion" : "1:1.30.1-7ubuntu3"
+ },
+ {
+ "name" : "usb-modeswitch-data",
+ "version" : "20191128-4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "20191128-4"
+ },
+ {
+ "name" : "libssl3",
+ "version" : "3.0.2-0ubuntu1.6",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openssl",
+ "sourceVersion" : "3.0.2-0ubuntu1.6"
+ },
+ {
+ "name" : "libblockdev-fs2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "libfwupd2",
+ "version" : "1.7.5-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fwupd",
+ "sourceVersion" : "1.7.5-3"
+ },
+ {
+ "name" : "file",
+ "version" : "1:5.41-3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:5.41-3"
+ },
+ {
+ "name" : "libuchardet0",
+ "version" : "0.0.7-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "uchardet",
+ "sourceVersion" : "0.0.7-1build2"
+ },
+ {
+ "name" : "info",
+ "version" : "6.8-4build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "texinfo",
+ "sourceVersion" : "6.8-4build1"
+ },
+ {
+ "name" : "vim-tiny",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "vim",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "gcc-12-base",
+ "version" : "12-20220319-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gcc-12",
+ "sourceVersion" : "12-20220319-1ubuntu1"
+ },
+ {
+ "name" : "libxmuu1",
+ "version" : "2:1.1.3-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxmu",
+ "sourceVersion" : "2:1.1.3-3"
+ },
+ {
+ "name" : "python3-distupgrade",
+ "version" : "1:22.4.11",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-release-upgrader",
+ "sourceVersion" : "1:22.4.11"
+ },
+ {
+ "name" : "python3-commandnotfound",
+ "version" : "22.4.0",
+ "publisher" : "Ubuntu",
+ "sourceName" : "command-not-found",
+ "sourceVersion" : "22.4.0"
+ },
+ {
+ "name" : "grub-pc-bin",
+ "version" : "2.6-2ubuntu7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "grub2",
+ "sourceVersion" : "2.6-2ubuntu7"
+ },
+ {
+ "name" : "python3-minimal",
+ "version" : "3.10.4-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-defaults",
+ "sourceVersion" : "3.10.4-0ubuntu2"
+ },
+ {
+ "name" : "patch",
+ "version" : "2.7.6-7build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.7.6-7build2"
+ },
+ {
+ "name" : "libsepol2",
+ "version" : "3.3-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsepol",
+ "sourceVersion" : "3.3-1build1"
+ },
+ {
+ "name" : "libinih1",
+ "version" : "53-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libinih",
+ "sourceVersion" : "53-1ubuntu3"
+ },
+ {
+ "name" : "udev",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "init",
+ "version" : "1.62",
+ "publisher" : "Ubuntu",
+ "sourceName" : "init-system-helpers",
+ "sourceVersion" : "1.62"
+ },
+ {
+ "name" : "libtinfo6",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libefivar1",
+ "version" : "37-6ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "efivar",
+ "sourceVersion" : "37-6ubuntu2"
+ },
+ {
+ "name" : "libpam-modules",
+ "version" : "1.4.0-11ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pam",
+ "sourceVersion" : "1.4.0-11ubuntu2"
+ },
+ {
+ "name" : "libjson-c5",
+ "version" : "0.15-3~ubuntu1.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "json-c",
+ "sourceVersion" : "0.15-3~ubuntu1.22.4.1"
+ },
+ {
+ "name" : "libsemanage-common",
+ "version" : "3.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsemanage",
+ "sourceVersion" : "3.3-1build2"
+ },
+ {
+ "name" : "libhogweed6",
+ "version" : "3.7.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nettle",
+ "sourceVersion" : "3.7.3-1build2"
+ },
+ {
+ "name" : "libjson-glib-1.0-0",
+ "version" : "1.6.6-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "json-glib",
+ "sourceVersion" : "1.6.6-1build1"
+ },
+ {
+ "name" : "libbz2-1.0",
+ "version" : "1.0.8-5build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bzip2",
+ "sourceVersion" : "1.0.8-5build1"
+ },
+ {
+ "name" : "ldapscripts",
+ "version" : "2.0.8-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.0.8-1ubuntu2"
+ },
+ {
+ "name" : "linux-image-5.15.0-41-generic",
+ "version" : "5.15.0-41.44",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux-signed",
+ "sourceVersion" : "5.15.0-41.44"
+ },
+ {
+ "name" : "libkrb5-3",
+ "version" : "1.19.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "krb5",
+ "sourceVersion" : "1.19.2-2"
+ },
+ {
+ "name" : "libpcap0.8",
+ "version" : "1.10.1-4build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libpcap",
+ "sourceVersion" : "1.10.1-4build1"
+ },
+ {
+ "name" : "grub-pc",
+ "version" : "2.6-2ubuntu7",
+ "publisher" : "Ubuntu",
+ "sourceName" : "grub2",
+ "sourceVersion" : "2.6-2ubuntu7"
+ },
+ {
+ "name" : "mawk",
+ "version" : "1.3.4.20200120-3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.3.4.20200120-3"
+ },
+ {
+ "name" : "libbpf0",
+ "version" : "1:0.5.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libbpf (0.5.0-1)",
+ "sourceVersion" : "0.5.0-1"
+ },
+ {
+ "name" : "libsqlite3-0",
+ "version" : "3.37.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "sqlite3",
+ "sourceVersion" : "3.37.2-2"
+ },
+ {
+ "name" : "libgcc-s1",
+ "version" : "12-20220319-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gcc-12",
+ "sourceVersion" : "12-20220319-1ubuntu1"
+ },
+ {
+ "name" : "libftdi1-2",
+ "version" : "1.5-5build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libftdi1",
+ "sourceVersion" : "1.5-5build3"
+ },
+ {
+ "name" : "libmbim-proxy",
+ "version" : "1.26.2-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmbim",
+ "sourceVersion" : "1.26.2-1build1"
+ },
+ {
+ "name" : "python3-update-manager",
+ "version" : "1:22.4.9",
+ "publisher" : "Ubuntu",
+ "sourceName" : "update-manager",
+ "sourceVersion" : "1:22.4.9"
+ },
+ {
+ "name" : "python3-idna",
+ "version" : "3.3-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-idna",
+ "sourceVersion" : "3.3-1"
+ },
+ {
+ "name" : "media-types",
+ "version" : "7.0.0",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "7.0.0"
+ },
+ {
+ "name" : "libpopt0",
+ "version" : "1.18-3build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "popt",
+ "sourceVersion" : "1.18-3build1"
+ },
+ {
+ "name" : "ubuntu-keyring",
+ "version" : "2021.3.26",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2021.3.26"
+ },
+ {
+ "name" : "libgpgme11",
+ "version" : "1.16.0-1.2ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gpgme1.0",
+ "sourceVersion" : "1.16.0-1.2ubuntu4"
+ },
+ {
+ "name" : "ufw",
+ "version" : "0.36.1-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.36.1-4build1"
+ },
+ {
+ "name" : "python3-openssl",
+ "version" : "21.0.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyopenssl",
+ "sourceVersion" : "21.0.0-1"
+ },
+ {
+ "name" : "python3-bcrypt",
+ "version" : "3.2.0-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-bcrypt",
+ "sourceVersion" : "3.2.0-1build1"
+ },
+ {
+ "name" : "vim-runtime",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "vim",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "libpipeline1",
+ "version" : "1.5.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libpipeline",
+ "sourceVersion" : "1.5.5-1"
+ },
+ {
+ "name" : "libfreetype6",
+ "version" : "2.11.1+dfsg-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "freetype",
+ "sourceVersion" : "2.11.1+dfsg-1build1"
+ },
+ {
+ "name" : "python3-automat",
+ "version" : "20.2.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "automat",
+ "sourceVersion" : "20.2.0-1"
+ },
+ {
+ "name" : "finalrd",
+ "version" : "9build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "9build1"
+ },
+ {
+ "name" : "jq",
+ "version" : "1.6-2.1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.6-2.1ubuntu3"
+ },
+ {
+ "name" : "libldap-2.5-0",
+ "version" : "2.5.13+dfsg-0ubuntu0.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openldap",
+ "sourceVersion" : "2.5.13+dfsg-0ubuntu0.22.4.1"
+ },
+ {
+ "name" : "cryptsetup-bin",
+ "version" : "2:2.4.3-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cryptsetup",
+ "sourceVersion" : "2:2.4.3-1ubuntu1"
+ },
+ {
+ "name" : "sed",
+ "version" : "4.8-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.8-1ubuntu2"
+ },
+ {
+ "name" : "libmnl0",
+ "version" : "1.0.4-3build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmnl",
+ "sourceVersion" : "1.0.4-3build2"
+ },
+ {
+ "name" : "libisns0",
+ "version" : "0.101-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "open-isns",
+ "sourceVersion" : "0.101-0ubuntu2"
+ },
+ {
+ "name" : "libmaxminddb0",
+ "version" : "1.5.2-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmaxminddb",
+ "sourceVersion" : "1.5.2-1build2"
+ },
+ {
+ "name" : "libapt-pkg6.0",
+ "version" : "2.4.5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apt",
+ "sourceVersion" : "2.4.5"
+ },
+ {
+ "name" : "fwupd-signed",
+ "version" : "1.44+1.2-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fwupd-signed (1.44)",
+ "sourceVersion" : "1.44"
+ },
+ {
+ "name" : "hostname",
+ "version" : "3.23ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.23ubuntu2"
+ },
+ {
+ "name" : "libsodium23",
+ "version" : "1.0.18-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsodium",
+ "sourceVersion" : "1.0.18-1build2"
+ },
+ {
+ "name" : "gpgv",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "python3.10",
+ "version" : "3.10.4-3ubuntu0.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "3.10.4-3ubuntu0.1"
+ },
+ {
+ "name" : "man-db",
+ "version" : "2.10.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.10.2-1"
+ },
+ {
+ "name" : "libmm-glib0",
+ "version" : "1.18.6-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "modemmanager",
+ "sourceVersion" : "1.18.6-1"
+ },
+ {
+ "name" : "update-manager-core",
+ "version" : "1:22.4.9",
+ "publisher" : "Ubuntu",
+ "sourceName" : "update-manager",
+ "sourceVersion" : "1:22.4.9"
+ },
+ {
+ "name" : "python3-launchpadlib",
+ "version" : "1.10.16-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-launchpadlib",
+ "sourceVersion" : "1.10.16-1"
+ },
+ {
+ "name" : "fwupd",
+ "version" : "1.7.5-3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.7.5-3"
+ },
+ {
+ "name" : "sharutils",
+ "version" : "1:4.15.2-5build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:4.15.2-5build1"
+ },
+ {
+ "name" : "python3-jwt",
+ "version" : "2.3.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pyjwt",
+ "sourceVersion" : "2.3.0-1"
+ },
+ {
+ "name" : "libproc-processtable-perl",
+ "version" : "0.634-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.634-1build1"
+ },
+ {
+ "name" : "python3-twisted",
+ "version" : "22.1.0-2ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "twisted",
+ "sourceVersion" : "22.1.0-2ubuntu2.1"
+ },
+ {
+ "name" : "libpcre3",
+ "version" : "2:8.39-13ubuntu0.22.4.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pcre3",
+ "sourceVersion" : "2:8.39-13ubuntu0.22.4.1"
+ },
+ {
+ "name" : "libmpdec3",
+ "version" : "2.5.1-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "mpdecimal",
+ "sourceVersion" : "2.5.1-2build2"
+ },
+ {
+ "name" : "libgstreamer1.0-0",
+ "version" : "1.20.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gstreamer1.0",
+ "sourceVersion" : "1.20.1-1"
+ },
+ {
+ "name" : "linux-modules-5.15.0-41-generic",
+ "version" : "5.15.0-41.44",
+ "publisher" : "Ubuntu",
+ "sourceName" : "linux",
+ "sourceVersion" : "5.15.0-41.44"
+ },
+ {
+ "name" : "libdns-export1110",
+ "version" : "1:9.11.19+dfsg-2.1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9-libs",
+ "sourceVersion" : "1:9.11.19+dfsg-2.1ubuntu3"
+ },
+ {
+ "name" : "fonts-ubuntu-console",
+ "version" : "0.83-6ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "fonts-ubuntu",
+ "sourceVersion" : "0.83-6ubuntu1"
+ },
+ {
+ "name" : "gpg-agent",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libpolkit-agent-1-0",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceName" : "policykit-1",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "libmspack0",
+ "version" : "0.10.1-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libmspack",
+ "sourceVersion" : "0.10.1-2build2"
+ },
+ {
+ "name" : "cloud-initramfs-copymods",
+ "version" : "0.47ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cloud-initramfs-tools",
+ "sourceVersion" : "0.47ubuntu1"
+ },
+ {
+ "name" : "python3-hyperlink",
+ "version" : "21.0.0-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "hyperlink",
+ "sourceVersion" : "21.0.0-3"
+ },
+ {
+ "name" : "iso-codes",
+ "version" : "4.9.0-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.9.0-1"
+ },
+ {
+ "name" : "kpartx",
+ "version" : "0.8.8-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "multipath-tools",
+ "sourceVersion" : "0.8.8-1ubuntu1"
+ },
+ {
+ "name" : "tar",
+ "version" : "1.34+dfsg-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.34+dfsg-1build3"
+ },
+ {
+ "name" : "openssh-client",
+ "version" : "1:8.9p1-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "openssh",
+ "sourceVersion" : "1:8.9p1-3"
+ },
+ {
+ "name" : "pci.ids",
+ "version" : "0.0~2022.1.22-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.0~2022.1.22-1"
+ },
+ {
+ "name" : "python3-lib2to3",
+ "version" : "3.10.4-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-stdlib-extensions",
+ "sourceVersion" : "3.10.4-0ubuntu1"
+ },
+ {
+ "name" : "libtirpc3",
+ "version" : "1.3.2-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libtirpc",
+ "sourceVersion" : "1.3.2-2build1"
+ },
+ {
+ "name" : "python3-software-properties",
+ "version" : "0.99.22.2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "software-properties",
+ "sourceVersion" : "0.99.22.2"
+ },
+ {
+ "name" : "libsemanage2",
+ "version" : "3.3-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsemanage",
+ "sourceVersion" : "3.3-1build2"
+ },
+ {
+ "name" : "python3-service-identity",
+ "version" : "18.1.0-6",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-service-identity",
+ "sourceVersion" : "18.1.0-6"
+ },
+ {
+ "name" : "sensible-utils",
+ "version" : "0.0.17",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.0.17"
+ },
+ {
+ "name" : "login",
+ "version" : "1:4.8.1-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "shadow",
+ "sourceVersion" : "1:4.8.1-2ubuntu2"
+ },
+ {
+ "name" : "gdisk",
+ "version" : "1.0.8-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.0.8-4build1"
+ },
+ {
+ "name" : "lsof",
+ "version" : "4.93.2+dfsg-1.1build2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.93.2+dfsg-1.1build2"
+ },
+ {
+ "name" : "open-vm-tools",
+ "version" : "2:11.3.5-1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2:11.3.5-1ubuntu4"
+ },
+ {
+ "name" : "libjansson4",
+ "version" : "2.13.1-1.1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "jansson",
+ "sourceVersion" : "2.13.1-1.1build3"
+ },
+ {
+ "name" : "ubuntu-release-upgrader-core",
+ "version" : "1:22.4.11",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ubuntu-release-upgrader",
+ "sourceVersion" : "1:22.4.11"
+ },
+ {
+ "name" : "passwd",
+ "version" : "1:4.8.1-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "shadow",
+ "sourceVersion" : "1:4.8.1-2ubuntu2"
+ },
+ {
+ "name" : "libnss-systemd",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "util-linux",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libarchive13",
+ "version" : "3.6.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libarchive",
+ "sourceVersion" : "3.6.0-1ubuntu1"
+ },
+ {
+ "name" : "gnupg",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "lshw",
+ "version" : "2.19.git.2021.6.19.996aaad9c7-2build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.19.git.2021.6.19.996aaad9c7-2build1"
+ },
+ {
+ "name" : "diffutils",
+ "version" : "1:3.8-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:3.8-0ubuntu2"
+ },
+ {
+ "name" : "libcurl4",
+ "version" : "7.81.0-1ubuntu1.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "curl",
+ "sourceVersion" : "7.81.0-1ubuntu1.4"
+ },
+ {
+ "name" : "publicsuffix",
+ "version" : "20211207.1025-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "20211207.1025-1"
+ },
+ {
+ "name" : "os-prober",
+ "version" : "1.79ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.79ubuntu2"
+ },
+ {
+ "name" : "libpng16-16",
+ "version" : "1.6.37-3build5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libpng1.6",
+ "sourceVersion" : "1.6.37-3build5"
+ },
+ {
+ "name" : "friendly-recovery",
+ "version" : "0.2.42",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.2.42"
+ },
+ {
+ "name" : "telnet",
+ "version" : "0.17-44build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "netkit-telnet",
+ "sourceVersion" : "0.17-44build1"
+ },
+ {
+ "name" : "bash-completion",
+ "version" : "1:2.11-5ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:2.11-5ubuntu1"
+ },
+ {
+ "name" : "libncursesw6",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "python3-systemd",
+ "version" : "234-3ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-systemd",
+ "sourceVersion" : "234-3ubuntu2"
+ },
+ {
+ "name" : "groff-base",
+ "version" : "1.22.4-8build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "groff",
+ "sourceVersion" : "1.22.4-8build1"
+ },
+ {
+ "name" : "libx11-6",
+ "version" : "2:1.7.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libx11",
+ "sourceVersion" : "2:1.7.5-1"
+ },
+ {
+ "name" : "mdadm",
+ "version" : "4.2-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.2-0ubuntu1"
+ },
+ {
+ "name" : "bind9-host",
+ "version" : "1:9.18.1-1ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9",
+ "sourceVersion" : "1:9.18.1-1ubuntu1.1"
+ },
+ {
+ "name" : "libblockdev-part2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "bash",
+ "version" : "5.1-6ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.1-6ubuntu1"
+ },
+ {
+ "name" : "whiptail",
+ "version" : "0.52.21-5ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "newt",
+ "sourceVersion" : "0.52.21-5ubuntu2"
+ },
+ {
+ "name" : "libtss2-tcti-mssim0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "dosfstools",
+ "version" : "4.2-1build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.2-1build3"
+ },
+ {
+ "name" : "libudev1",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "python3",
+ "version" : "3.10.4-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python3-defaults",
+ "sourceVersion" : "3.10.4-0ubuntu2"
+ },
+ {
+ "name" : "perl",
+ "version" : "5.34.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.34.0-3ubuntu1"
+ },
+ {
+ "name" : "libappstream4",
+ "version" : "0.15.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "appstream",
+ "sourceVersion" : "0.15.2-2"
+ },
+ {
+ "name" : "libnghttp2-14",
+ "version" : "1.43.0-1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "nghttp2",
+ "sourceVersion" : "1.43.0-1build3"
+ },
+ {
+ "name" : "networkd-dispatcher",
+ "version" : "2.1-2ubuntu0.22.4.2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "2.1-2ubuntu0.22.4.2"
+ },
+ {
+ "name" : "libusb-1.0-0",
+ "version" : "2:1.0.25-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libusb-1.0",
+ "sourceVersion" : "2:1.0.25-1ubuntu2"
+ },
+ {
+ "name" : "netplan.io",
+ "version" : "0.104-0ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.104-0ubuntu2"
+ },
+ {
+ "name" : "ntfs-3g",
+ "version" : "1:2021.8.22-3ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:2021.8.22-3ubuntu1.1"
+ },
+ {
+ "name" : "ncurses-base",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libpci3",
+ "version" : "1:3.7.0-6",
+ "publisher" : "Ubuntu",
+ "sourceName" : "pciutils",
+ "sourceVersion" : "1:3.7.0-6"
+ },
+ {
+ "name" : "python3-jsonpatch",
+ "version" : "1.32-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-json-patch",
+ "sourceVersion" : "1.32-2"
+ },
+ {
+ "name" : "libtss2-tcti-cmd0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "libss2",
+ "version" : "1.46.5-2ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "e2fsprogs",
+ "sourceVersion" : "1.46.5-2ubuntu1.1"
+ },
+ {
+ "name" : "readline-common",
+ "version" : "8.1.2-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "readline",
+ "sourceVersion" : "8.1.2-1"
+ },
+ {
+ "name" : "libncurses6",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libk5crypto3",
+ "version" : "1.19.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "krb5",
+ "sourceVersion" : "1.19.2-2"
+ },
+ {
+ "name" : "python3-cffi-backend",
+ "version" : "1.15.0-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-cffi",
+ "sourceVersion" : "1.15.0-1build2"
+ },
+ {
+ "name" : "python3-incremental",
+ "version" : "21.3.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "incremental",
+ "sourceVersion" : "21.3.0-1"
+ },
+ {
+ "name" : "libatasmart4",
+ "version" : "0.19-5build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libatasmart",
+ "sourceVersion" : "0.19-5build2"
+ },
+ {
+ "name" : "git-man",
+ "version" : "1:2.34.1-1ubuntu1.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "git",
+ "sourceVersion" : "1:2.34.1-1ubuntu1.4"
+ },
+ {
+ "name" : "libuuid1",
+ "version" : "2.37.2-4ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "util-linux",
+ "sourceVersion" : "2.37.2-4ubuntu3"
+ },
+ {
+ "name" : "libidn2-0",
+ "version" : "2.3.2-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libidn2",
+ "sourceVersion" : "2.3.2-2build1"
+ },
+ {
+ "name" : "python3-attr",
+ "version" : "21.2.0-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-attrs",
+ "sourceVersion" : "21.2.0-1"
+ },
+ {
+ "name" : "vim-common",
+ "version" : "2:8.2.3995-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "vim",
+ "sourceVersion" : "2:8.2.3995-1ubuntu2"
+ },
+ {
+ "name" : "bind9-libs",
+ "version" : "1:9.18.1-1ubuntu1.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "bind9",
+ "sourceVersion" : "1:9.18.1-1ubuntu1.1"
+ },
+ {
+ "name" : "findutils",
+ "version" : "4.8.0-1ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "4.8.0-1ubuntu3"
+ },
+ {
+ "name" : "sbsigntool",
+ "version" : "0.9.4-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.9.4-2ubuntu2"
+ },
+ {
+ "name" : "usbutils",
+ "version" : "1:14-1build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1:14-1build1"
+ },
+ {
+ "name" : "libblockdev-part-err2",
+ "version" : "2.26-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libblockdev",
+ "sourceVersion" : "2.26-1"
+ },
+ {
+ "name" : "libtext-iconv-perl",
+ "version" : "1.7-7build3",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.7-7build3"
+ },
+ {
+ "name" : "libsmbios-c2",
+ "version" : "2.4.3-1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libsmbios",
+ "sourceVersion" : "2.4.3-1build1"
+ },
+ {
+ "name" : "python3-apport",
+ "version" : "2.20.11-0ubuntu82.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "apport",
+ "sourceVersion" : "2.20.11-0ubuntu82.1"
+ },
+ {
+ "name" : "lxd-agent-loader",
+ "version" : "0.5",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "0.5"
+ },
+ {
+ "name" : "nano",
+ "version" : "6.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "6.2-1"
+ },
+ {
+ "name" : "console-setup-linux",
+ "version" : "1.205ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "console-setup",
+ "sourceVersion" : "1.205ubuntu3"
+ },
+ {
+ "name" : "gettext-base",
+ "version" : "0.21-4ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gettext",
+ "sourceVersion" : "0.21-4ubuntu4"
+ },
+ {
+ "name" : "libpolkit-gobject-1-0",
+ "version" : "0.105-33",
+ "publisher" : "Ubuntu",
+ "sourceName" : "policykit-1",
+ "sourceVersion" : "0.105-33"
+ },
+ {
+ "name" : "python3-httplib2",
+ "version" : "0.20.2-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-httplib2",
+ "sourceVersion" : "0.20.2-2"
+ },
+ {
+ "name" : "initramfs-tools-core",
+ "version" : "0.140ubuntu13",
+ "publisher" : "Ubuntu",
+ "sourceName" : "initramfs-tools",
+ "sourceVersion" : "0.140ubuntu13"
+ },
+ {
+ "name" : "libdebconfclient0",
+ "version" : "0.261ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "cdebconf",
+ "sourceVersion" : "0.261ubuntu1"
+ },
+ {
+ "name" : "gir1.2-packagekitglib-1.0",
+ "version" : "1.2.5-2ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "packagekit",
+ "sourceVersion" : "1.2.5-2ubuntu2"
+ },
+ {
+ "name" : "libbsd0",
+ "version" : "0.11.5-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libbsd",
+ "sourceVersion" : "0.11.5-1"
+ },
+ {
+ "name" : "libunwind8",
+ "version" : "1.3.2-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libunwind",
+ "sourceVersion" : "1.3.2-2build2"
+ },
+ {
+ "name" : "python3-blinker",
+ "version" : "1.4+dfsg1-0.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "blinker",
+ "sourceVersion" : "1.4+dfsg1-0.4"
+ },
+ {
+ "name" : "libc-bin",
+ "version" : "2.35-0ubuntu3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "glibc",
+ "sourceVersion" : "2.35-0ubuntu3"
+ },
+ {
+ "name" : "ncurses-bin",
+ "version" : "6.3-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "ncurses",
+ "sourceVersion" : "6.3-2"
+ },
+ {
+ "name" : "libcap2-bin",
+ "version" : "1:2.44-1build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libcap2",
+ "sourceVersion" : "1:2.44-1build3"
+ },
+ {
+ "name" : "gnupg-l10n",
+ "version" : "2.2.27-3ubuntu2.1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gnupg2",
+ "sourceVersion" : "2.2.27-3ubuntu2.1"
+ },
+ {
+ "name" : "libeatmydata1",
+ "version" : "130-2build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libeatmydata",
+ "sourceVersion" : "130-2build1"
+ },
+ {
+ "name" : "libxdmcp6",
+ "version" : "1:1.1.3-0ubuntu5",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libxdmcp",
+ "sourceVersion" : "1:1.1.3-0ubuntu5"
+ },
+ {
+ "name" : "libutempter0",
+ "version" : "1.2.1-2build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libutempter",
+ "sourceVersion" : "1.2.1-2build2"
+ },
+ {
+ "name" : "python3-constantly",
+ "version" : "15.1.0-2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "constantly",
+ "sourceVersion" : "15.1.0-2"
+ },
+ {
+ "name" : "libbinutils",
+ "version" : "2.38-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "binutils",
+ "sourceVersion" : "2.38-3ubuntu1"
+ },
+ {
+ "name" : "libstdc++6",
+ "version" : "12-20220319-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gcc-12",
+ "sourceVersion" : "12-20220319-1ubuntu1"
+ },
+ {
+ "name" : "btrfs-progs",
+ "version" : "5.16.2-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.16.2-1"
+ },
+ {
+ "name" : "python3-pyasn1-modules",
+ "version" : "0.2.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-pyasn1-modules",
+ "sourceVersion" : "0.2.1-1"
+ },
+ {
+ "name" : "python3-six",
+ "version" : "1.16.0-3ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "six",
+ "sourceVersion" : "1.16.0-3ubuntu1"
+ },
+ {
+ "name" : "libmpfr6",
+ "version" : "4.1.0-3build3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "mpfr4",
+ "sourceVersion" : "4.1.0-3build3"
+ },
+ {
+ "name" : "libtss2-mu0",
+ "version" : "3.2.0-1ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "tpm2-tss",
+ "sourceVersion" : "3.2.0-1ubuntu1"
+ },
+ {
+ "name" : "run-one",
+ "version" : "1.17-0ubuntu1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "1.17-0ubuntu1"
+ },
+ {
+ "name" : "python3-distro-info",
+ "version" : "1.1build1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "distro-info",
+ "sourceVersion" : "1.1build1"
+ },
+ {
+ "name" : "libdevmapper1.02.1",
+ "version" : "2:1.2.175-2.1ubuntu4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "lvm2 (2.03.11-2.1ubuntu4)",
+ "sourceVersion" : "2.3.11-2.1ubuntu4"
+ },
+ {
+ "name" : "python3-cryptography",
+ "version" : "3.4.8-1ubuntu2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-cryptography",
+ "sourceVersion" : "3.4.8-1ubuntu2"
+ },
+ {
+ "name" : "libmagic1",
+ "version" : "1:5.41-3",
+ "publisher" : "Ubuntu",
+ "sourceName" : "file",
+ "sourceVersion" : "1:5.41-3"
+ },
+ {
+ "name" : "zsh",
+ "version" : "5.8.1-1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "5.8.1-1"
+ },
+ {
+ "name" : "tnftp",
+ "version" : "20210827-4build1",
+ "publisher" : "Ubuntu",
+ "sourceVersion" : "20210827-4build1"
+ },
+ {
+ "name" : "libsystemd0",
+ "version" : "249.11-0ubuntu3.4",
+ "publisher" : "Ubuntu",
+ "sourceName" : "systemd",
+ "sourceVersion" : "249.11-0ubuntu3.4"
+ },
+ {
+ "name" : "python3-tz",
+ "version" : "2022.1-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "python-tz",
+ "sourceVersion" : "2022.1-1"
+ },
+ {
+ "name" : "libgdbm-compat4",
+ "version" : "1.23-1",
+ "publisher" : "Ubuntu",
+ "sourceName" : "gdbm",
+ "sourceVersion" : "1.23-1"
+ },
+ {
+ "name" : "libyaml-0-2",
+ "version" : "0.2.2-1build2",
+ "publisher" : "Ubuntu",
+ "sourceName" : "libyaml",
+ "sourceVersion" : "0.2.2-1build2"
+ }
+ ],
+ "softwareUpdate" : [
+ {
+ "name" : "snapd",
+ "version" : "2.56.2+22.04ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-software-properties",
+ "version" : "0.99.22.3",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "software-properties-common",
+ "version" : "0.99.22.3",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libldap-common",
+ "version" : "2.5.13+dfsg-0ubuntu0.22.04.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libgstreamer1.0-0",
+ "version" : "1.20.3-0ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup-bin",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "cryptsetup-initramfs",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-distupgrade",
+ "version" : "1:22.04.13",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "ubuntu-release-upgrader-core",
+ "version" : "1:22.04.13",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libnftables1",
+ "version" : "1.0.2-1ubuntu3",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "nftables",
+ "version" : "1.0.2-1ubuntu3",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "ubuntu-advantage-tools",
+ "version" : "27.10.1~22.04.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-gi",
+ "version" : "3.42.1-0ubuntu1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python3-apt",
+ "version" : "2.3.0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "python-apt-common",
+ "version" : "2.3.0ubuntu2.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "locales",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libnetplan0",
+ "version" : "0.104-0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "netplan.io",
+ "version" : "0.104-0ubuntu2.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libcryptsetup12",
+ "version" : "2:2.4.3-1ubuntu1.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "isc-dhcp-common",
+ "version" : "4.4.1-2.3ubuntu2.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "isc-dhcp-client",
+ "version" : "4.4.1-2.3ubuntu2.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "apt-utils",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "apt",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libapt-pkg6.0",
+ "version" : "2.4.7",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libc-bin",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "base-files",
+ "version" : "12ubuntu4.2",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "libc6",
+ "version" : "2.35-0ubuntu3.1",
+ "arch" : "amd64",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ },
+ {
+ "name" : "motd-news-config",
+ "version" : "12ubuntu4.2",
+ "arch" : "all",
+ "from" : "apt-get",
+ "kind" : "none",
+ "source" : "Ubuntu:22.04/jammy-updates"
+ }
+ ],
+ "sounds" : [
+ {
+ "name" : "Multimedia audio controller - Intel Corporation 82801AA AC'97 Audio Controller",
+ "description" : "rev 01",
+ "quantity" : 1
+ }
+ ],
+ "storages" : [
+ {
+ "name" : "sda",
+ "description" : "SCSI",
+ "size" : 45035290624,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "serialNumber" : "a23d4170",
+ "sType" : "disk",
+ "quantity" : 1
+ },
+ {
+ "name" : "sdb",
+ "description" : "SCSI",
+ "size" : 10485760,
+ "firmware" : "10",
+ "manufacturer" : "VBOX",
+ "model" : "HARDDISK",
+ "sType" : "disk",
+ "quantity" : 1
+ }
+ ],
+ "videos" : [],
+ "vms" : []
+ }
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/JsonSpecMatcher.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/JsonSpecMatcher.scala
index 9d4f44ad154..c2f3a2f0fd0 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/JsonSpecMatcher.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/JsonSpecMatcher.scala
@@ -18,9 +18,9 @@ trait JsonSpecMatcher { self: MustMatchers with Specification =>
/**
* Tests for a non-strict equality of json, by comparing the string.
- * Ignores any whitespace between words, non-words, and lines
+ * Ignores any whitespace between words, non-words, and lines
* i.e. additional whitespace in either side would still make the test pass.
- *
+ *
* Displays the actual and expected json in the error message in a pretty format.
*/
def equalsJson(res: String): Matcher[String] = {
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/MockServices.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/MockServices.scala
index 6978658e2d2..d9089588b64 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/MockServices.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/MockServices.scala
@@ -54,8 +54,8 @@ import com.normation.inventory.domain.AgentType.CfeCommunity
import com.normation.inventory.ldap.core.InventoryDit
import com.normation.inventory.ldap.core.InventoryDitService
import com.normation.inventory.ldap.core.InventoryDitServiceImpl
-import com.normation.inventory.ldap.core.LDAPFullInventoryRepository
import com.normation.inventory.services.core.ReadOnlySoftwareDAO
+import com.normation.rudder.MockNodes.allNodeFacts
import com.normation.rudder.campaigns.Campaign
import com.normation.rudder.campaigns.CampaignDetails
import com.normation.rudder.campaigns.CampaignEvent
@@ -98,7 +98,19 @@ import com.normation.rudder.domain.properties.PropertyProvider
import com.normation.rudder.domain.queries._
import com.normation.rudder.domain.queries.CriterionComposition
import com.normation.rudder.domain.reports.NodeModeConfig
-import com.normation.rudder.domain.servers.Srv
+import com.normation.rudder.facts.nodes.ChangeContext
+import com.normation.rudder.facts.nodes.CoreNodeFact
+import com.normation.rudder.facts.nodes.CoreNodeFactRepository
+import com.normation.rudder.facts.nodes.NodeFact
+import com.normation.rudder.facts.nodes.NodeFactFullInventoryRepositoryProxy
+import com.normation.rudder.facts.nodes.NodeFactStorage
+import com.normation.rudder.facts.nodes.NodeInfoServiceProxy
+import com.normation.rudder.facts.nodes.SelectFacts
+import com.normation.rudder.facts.nodes.SoftDaoGetNodesbySofwareName
+import com.normation.rudder.facts.nodes.StorageChangeEventDelete
+import com.normation.rudder.facts.nodes.StorageChangeEventSave
+import com.normation.rudder.facts.nodes.StorageChangeEventStatus
+import com.normation.rudder.facts.nodes.WoFactNodeRepositoryProxy
import com.normation.rudder.git.GitFindUtils
import com.normation.rudder.git.GitRepositoryProviderImpl
import com.normation.rudder.git.GitRevisionProvider
@@ -116,7 +128,6 @@ import com.normation.rudder.rule.category._
import com.normation.rudder.services.marshalling.NodeGroupCategoryUnserialisationImpl
import com.normation.rudder.services.marshalling.NodeGroupUnserialisationImpl
import com.normation.rudder.services.marshalling.RuleUnserialisationImpl
-import com.normation.rudder.services.nodes.NodeInfoService
import com.normation.rudder.services.policies.NodeConfigData
import com.normation.rudder.services.policies.NodeConfiguration
import com.normation.rudder.services.policies.ParameterForConfiguration
@@ -124,6 +135,7 @@ import com.normation.rudder.services.policies.Policy
import com.normation.rudder.services.policies.SystemVariableServiceImpl
import com.normation.rudder.services.queries._
import com.normation.rudder.services.servers.AllowedNetwork
+import com.normation.rudder.services.servers.FactListNewNodes
import com.normation.rudder.services.servers.NewNodeManager
import com.normation.rudder.services.servers.PolicyServerManagementService
import com.normation.rudder.services.servers.PolicyServers
@@ -132,6 +144,7 @@ import com.normation.rudder.services.servers.RelaySynchronizationMethod.Classic
import com.normation.utils.DateFormaterService
import com.normation.utils.StringUuidGeneratorImpl
import com.normation.zio._
+import com.softwaremill.quicklens._
import com.unboundid.ldap.sdk.DN
import com.unboundid.ldap.sdk.RDN
import com.unboundid.ldif.LDIFChangeRecord
@@ -142,13 +155,13 @@ import org.eclipse.jgit.lib.ObjectId
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
import scala.annotation.tailrec
-import scala.collection.SortedMap
-import scala.collection.immutable
+import scala.collection.immutable.{SortedMap => ISortedMap}
import scala.util.control.NonFatal
import scala.xml.Elem
import zio.{System => _, Tag => _, _}
import zio.json.jsonDiscriminator
import zio.json.jsonHint
+import zio.stream.ZStream
import zio.syntax._
/*
@@ -748,14 +761,16 @@ class MockDirectives(mockTechniques: MockTechniques) {
override def getActiveTechniqueByCategory(
includeSystem: Boolean
- ): IOResult[SortedMap[List[ActiveTechniqueCategoryId], CategoryWithActiveTechniques]] = {
+ ): IOResult[ISortedMap[List[ActiveTechniqueCategoryId], CategoryWithActiveTechniques]] = {
implicit val ordering = ActiveTechniqueCategoryOrdering
- rootActiveTechniqueCategory.get.map(_.fullIndex.map {
- case (path, fat) =>
- (
- path,
- CategoryWithActiveTechniques(fat.toActiveTechniqueCategory(), fat.activeTechniques.map(_.toActiveTechnique()).toSet)
- )
+ rootActiveTechniqueCategory.get.map(c => {
+ ISortedMap.empty[List[ActiveTechniqueCategoryId], CategoryWithActiveTechniques] ++ c.fullIndex.map {
+ case (path, fat) =>
+ (
+ path,
+ CategoryWithActiveTechniques(fat.toActiveTechniqueCategory(), fat.activeTechniques.map(_.toActiveTechnique()).toSet)
+ )
+ }
})
}
@@ -1626,9 +1641,7 @@ final case class NodeBase(
deleted: Map[NodeId, NodeDetails]
)
-class MockNodes() {
- val t2 = System.currentTimeMillis()
-
+object MockNodes {
val softwares = List(
Software(SoftwareUuid("s00"), name = Some("s00"), version = Some(new Version("1.0"))),
Software(SoftwareUuid("s01"), name = Some("s01"), version = Some(new Version("1.0"))),
@@ -1772,10 +1785,22 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
processes = Seq(Process(54432, Some("/bin/true"), Some(34.5f), Some(4235))),
vms = Seq(),
networks = Seq(
- Network("enp0s3", None, InetAddressUtils.getAddressByName("10.0.2.15").toSeq, speed = Some("1000"), status = Some("Up"))
+ Network(
+ "enp0s3",
+ None,
+ InetAddressUtils.getAddressByName("10.0.2.15").toSeq,
+ speed = Some("1000"),
+ status = Some("Up")
+ )
),
- fileSystems =
- Seq(FileSystem("/", Some("ext4"), freeSpace = Some(MemorySize(12076449792L)), totalSpace = Some(MemorySize(55076449792L))))
+ fileSystems = Seq(
+ FileSystem(
+ "/",
+ Some("ext4"),
+ freeSpace = Some(MemorySize(12076449792L)),
+ totalSpace = Some(MemorySize(55076449792L))
+ )
+ )
)
val node1Node = Node(
@@ -1819,7 +1844,7 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
),
name = None,
description = None,
- ram = Some(MemorySize(100000)),
+ ram = Some(MemorySize(1460132)),
swap = Some(MemorySize(1000000)),
inventoryDate = None,
receiveDate = None,
@@ -1837,10 +1862,22 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
processes = Seq(Process(54432, Some("/bin/true"), Some(34.5f), Some(4235))),
vms = Seq(),
networks = Seq(
- Network("enp0s3", None, InetAddressUtils.getAddressByName("10.0.2.15").toSeq, speed = Some("1000"), status = Some("Up"))
+ Network(
+ "enp0s3",
+ None,
+ InetAddressUtils.getAddressByName("10.0.2.15").toSeq,
+ speed = Some("1000"),
+ status = Some("Up")
+ )
),
- fileSystems =
- Seq(FileSystem("/", Some("ext4"), freeSpace = Some(MemorySize(12076449792L)), totalSpace = Some(MemorySize(55076449792L))))
+ fileSystems = Seq(
+ FileSystem(
+ "/",
+ Some("ext4"),
+ freeSpace = Some(MemorySize(12076449792L)),
+ totalSpace = Some(MemorySize(55076449792L))
+ )
+ )
)
// node1 us a relay
@@ -1928,139 +1965,13 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
fileSystems = Seq()
)
- val allNodesInfo = Map(rootId -> root, node1.id -> node1, node2.id -> node2)
- // both nodeInfoService and repo, since we deal with the same underlying node objects
- object nodeInfoService extends NodeInfoService with LDAPFullInventoryRepository with WoNodeRepository {
-
- // node status is in inventory.main.
- val nodeBase = Ref.Synchronized
- .make(
- Map(
- (rootId, NodeDetails(root, rootInventory, None)),
- (node1.id, NodeDetails(node1, nodeInventory1, None)),
- (node2.id, NodeDetails(node2, nodeInventory2, None)),
- (dscNode1.id, NodeDetails(dscNode1, dscInventory1, None))
- )
- )
- .runNow
-
- def getGenericOne[A](id: NodeId, status: InventoryStatus, f: NodeDetails => Option[A]): IOResult[Option[A]] = {
- nodeBase.get.map(_.collectFirst { case (i, n) if (i == id && n.nInv.main.status == status && f(n).isDefined) => f(n).get })
- }
- def getGenericAll[A](status: InventoryStatus, f: NodeDetails => Option[A]): IOResult[Map[NodeId, A]] = {
- nodeBase.get.map(_.collect { case (id, n) if (n.nInv.main.status == status && f(n).isDefined) => (id, f(n).get) })
- }
- def _info(node: NodeDetails) = Option(node.info)
- def _fullInventory(node: NodeDetails) = Option(FullInventory(node.nInv, node.mInv))
-
- override def getNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = getGenericOne(nodeId, AcceptedInventory, _info)
-
- override def getAll(): IOResult[Map[NodeId, NodeInfo]] = getGenericAll(AcceptedInventory, _info)
-
- override def getNodeInfos(nodesId: Set[NodeId]): IOResult[Set[NodeInfo]] = ZIO.foreach(nodesId) { nodeId =>
- getGenericOne(nodeId, AcceptedInventory, _info).map(x => x.get)
- }
- override def getNodeInfosSeq(nodesId: Seq[NodeId]): IOResult[Seq[NodeInfo]] =
- ZIO.foreach(nodeIds)(nodeId => getGenericOne(nodeId, AcceptedInventory, _info).map(x => x.get)).map(_.toSeq)
- override def getAllNodes(): IOResult[Map[NodeId, Node]] = getAll().map(_.map(kv => (kv._1, kv._2.node)))
- override def getAllNodesIds(): IOResult[Set[NodeId]] = getAllNodes().map(_.keySet)
- override def getAllNodeInfos(): IOResult[Seq[NodeInfo]] = getAll().map(_.values.toSeq)
- override def getAllSystemNodeIds(): IOResult[Seq[NodeId]] = {
- nodeBase.get.map(_.collect { case (id, n) if (n.info.isSystem) => id }.toSeq)
- }
-
- override def getPendingNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = getGenericOne(nodeId, PendingInventory, _info)
- override def getPendingNodeInfos(): IOResult[Map[NodeId, NodeInfo]] = getGenericAll(PendingInventory, _info)
-
- override def getDeletedNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = getGenericOne(nodeId, RemovedInventory, _info)
- override def getDeletedNodeInfos(): IOResult[Map[NodeId, NodeInfo]] = getGenericAll(RemovedInventory, _info)
-
- override def get(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Option[FullInventory]] =
- getGenericOne(id, inventoryStatus, _fullInventory)
- override def get(id: NodeId): IOResult[Option[FullInventory]] = {
- nodeBase.get.map(_.collectFirst { case (id, n) if (id == id) => FullInventory(n.nInv, n.mInv) })
- }
-
- override def getMachineId(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Option[(MachineUuid, InventoryStatus)]] = {
- getGenericOne(id, inventoryStatus, n => n.mInv.map(x => (x.id, x.status)))
- }
-
- override def getAllInventories(inventoryStatus: InventoryStatus): IOResult[Map[NodeId, FullInventory]] =
- getGenericAll(inventoryStatus, _fullInventory)
-
- override def getInventories(inventoryStatus: InventoryStatus, nodeIds: Set[NodeId]): IOResult[Map[NodeId, FullInventory]] =
- getAllInventories(inventoryStatus).map(_.filter(x => nodeIds.contains(x._1)))
-
- override def getAllNodeInventories(inventoryStatus: InventoryStatus): IOResult[Map[NodeId, NodeInventory]] =
- getGenericAll(inventoryStatus, _fullInventory(_).map(_.node))
-
- override def save(serverAndMachine: FullInventory): IOResult[Seq[LDIFChangeRecord]] = {
-
- // logic is in LDAPEntityMapper#inventoryEntriesToNodeInfos
- def mainFromInventory(inv: FullInventory): NodeInfo = {
- NodeInfo(
- Node(inv),
- inv.node.main.hostname,
- inv.machine.map(m => MachineInfo(m.id, m.machineType, m.systemSerialNumber, m.manufacturer)),
- inv.node.main.osDetails,
- inv.node.serverIps.toList,
- inv.node.inventoryDate.getOrElse(new DateTime(0)),
- inv.node.main.keyStatus,
- inv.node.agents,
- inv.node.main.policyServerId,
- inv.node.main.rootUser,
- inv.node.archDescription,
- inv.node.ram,
- inv.node.timezone
- )
- }
- val id = serverAndMachine.node.main.id
-
- nodeBase
- .updateZIO(nodes => {
- (nodes.get(id) match {
- case None => // new node
- nodes + ((id, NodeDetails(mainFromInventory(serverAndMachine), serverAndMachine.node, serverAndMachine.machine)))
- case Some(NodeDetails(m, nInv, mInv)) => // only update inventory
- nodes + ((id, NodeDetails(m, serverAndMachine.node, serverAndMachine.machine)))
- }).succeed
- })
- .map(_ => Nil)
- }
-
- // not implemented yet
- override def getNumberOfManagedNodes: Int = ???
-
- override def delete(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
- override def move(id: NodeId, from: InventoryStatus, into: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
- override def moveNode(id: NodeId, from: InventoryStatus, into: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
-
- override def updateNode(node: Node, modId: ModificationId, actor: EventActor, reason: Option[String]): IOResult[Node] = {
- nodeBase.modifyZIO { nodes =>
- nodes.get(node.id) match {
- case None => Inconsistency(s"Node ${node.id.value} does not exists").fail
- case Some(n) =>
- import com.softwaremill.quicklens._
- val newN = n.modify(_.info.node).setTo(node)
- (node, (nodes + ((node.id, newN)))).succeed
- }
- }
- }
-
- override def createNode(node: Node, modId: ModificationId, actor: EventActor, reason: Option[String]): IOResult[Node] = ???
-
- override def deleteNode(node: Node, modId: ModificationId, actor: EventActor, reason: Option[String]): IOResult[Node] = ???
-
- override def updateNodeKeyInfo(
- nodeId: NodeId,
- agentKey: Option[SecurityToken],
- agentKeyStatus: Option[KeyStatus],
- modId: ModificationId,
- actor: EventActor,
- reason: Option[String]
- ): IOResult[Unit] = ???
- }
-
+ val allNodesInfo = Map(rootId -> root, node1.id -> node1, node2.id -> node2)
+ val allNodeFacts = Map(
+ rootId -> NodeFact.fromCompat(root, Right(FullInventory(rootInventory, None)), softwares.take(7)),
+ node1.id -> NodeFact.fromCompat(node1, Right(FullInventory(nodeInventory1, None)), softwares.drop(5).take(10)),
+ node2.id -> NodeFact.fromCompat(node2, Right(FullInventory(nodeInventory2, None)), softwares.drop(5).take(10)),
+ dscNode1.id -> NodeFact.fromCompat(dscNode1, Right(FullInventory(dscInventory1, None)), softwares.drop(5).take(7))
+ )
val defaultModesConfig = NodeModeConfig(
globalComplianceMode = GlobalComplianceMode(FullCompliance, 30),
nodeHeartbeatPeriod = None,
@@ -2103,35 +2014,118 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
/**
* Some more nodes
*/
- val nodeIds = (for {
- i <- 0 to 10
- } yield {
- NodeId(s"${i}")
- }).toSet
+ val nodeIds = (
+ for {
+ i <- 0 to 10
+ } yield {
+ NodeId(s"${i}")
+ }
+ ).toSet
def newNode(id: NodeId) =
Node(id, "", "", NodeState.Enabled, false, false, DateTime.now, ReportingConfiguration(None, None, None), Nil, None)
- val nodes = (Set(root, node1, node2) ++ nodeIds.map { id =>
- NodeInfo(
- newNode(id),
- s"Node-${id}",
- None,
- Linux(Debian, "Jessie", new Version("7.0"), None, new Version("3.2")),
- Nil,
- DateTime.now,
- UndefinedKey,
- Seq(AgentInfo(CfeCommunity, None, PublicKey("rsa public key"), Set())),
- NodeId("root"),
- "",
- None,
- None,
- None
- )
- }).map(n => (n.id, n)).toMap
+ val nodes = (
+ Set(root, node1, node2) ++ nodeIds.map { id =>
+ NodeInfo(
+ newNode(id),
+ s"Node-${id}",
+ None,
+ Linux(Debian, "Jessie", new Version("7.0"), None, new Version("3.2")),
+ Nil,
+ DateTime.now,
+ UndefinedKey,
+ Seq(AgentInfo(CfeCommunity, None, PublicKey("rsa public key"), Set())),
+ NodeId("root"),
+ "",
+ None,
+ None,
+ None
+ )
+ }
+ ).map(n => (n.id, n)).toMap
+
+}
+
+class MockNodes() {
+ val t2 = System.currentTimeMillis()
+
+ object nodeFactStorage extends NodeFactStorage {
+ val nodeFactBase = Ref.make(allNodeFacts).runNow
+
+ override def save(nodeFact: NodeFact)(implicit attrs: SelectFacts = SelectFacts.all): IOResult[StorageChangeEventSave] = {
+ nodeFactBase.modify { b =>
+ val opt = b.get(nodeFact.id)
+ val updated = SelectFacts.merge(nodeFact, opt)
+ (
+ opt match {
+ case Some(n) =>
+ StorageChangeEventSave.Updated(n, updated, attrs)
+ case None =>
+ StorageChangeEventSave.Created(nodeFact, attrs)
+ },
+ b + (nodeFact.id -> updated)
+ )
+ }
+ }
+
+ override def changeStatus(nodeId: NodeId, status: InventoryStatus): IOResult[StorageChangeEventStatus] = {
+ nodeFactBase.modify(base => {
+ base.get(nodeId) match {
+ case Some(n) =>
+ (StorageChangeEventStatus.Done(nodeId), base + (n.id -> n.modify(_.rudderSettings.status).setTo(status)))
+ case None => (StorageChangeEventStatus.Noop(nodeId), base)
+ }
+ })
+ }
+
+ override def delete(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[StorageChangeEventDelete] = {
+ nodeFactBase.modify(b => {
+ b.get(nodeId) match {
+ case Some(n) => (StorageChangeEventDelete.Deleted(n, attrs), b.removed(nodeId))
+ case None => (StorageChangeEventDelete.Noop(nodeId), b)
+ }
+ })
+ }
+
+ def get(nodeId: NodeId, status: InventoryStatus)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ nodeFactBase.get.map(_.get(nodeId) match {
+ case Some(n) if (n.rudderSettings.status == status) => Some(SelectFacts.mask(n))
+ case _ => None
+ })
+ }
+
+ override def getPending(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ get(nodeId, PendingInventory)
+ }
+
+ override def getAccepted(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ get(nodeId, AcceptedInventory)
+ }
+
+ def getAll(status: InventoryStatus)(implicit attrs: SelectFacts): IOStream[NodeFact] = {
+ ZStream
+ .fromZIO(
+ nodeFactBase.get.map(base => {
+ ZStream.fromIterable(base.collect {
+ case (_, f) if (f.rudderSettings.status == status) => SelectFacts.mask(f)
+ })
+ })
+ )
+ .flatten
+ }
+
+ override def getAllPending()(implicit attrs: SelectFacts): IOStream[NodeFact] = {
+ getAll(PendingInventory)
+ }
+
+ override def getAllAccepted()(implicit attrs: SelectFacts): IOStream[NodeFact] = {
+ getAll(AcceptedInventory)
+ }
+ }
object softwareDao extends ReadOnlySoftwareDAO {
- val softRef = Ref.Synchronized.make(softwares.map(s => (s.id, s)).toMap).runNow
+ val softRef = Ref.Synchronized.make(MockNodes.softwares.map(s => (s.id, s)).toMap).runNow
override def getSoftware(ids: Seq[SoftwareUuid]): IOResult[Seq[Software]] = {
softRef.get.map(_.map(_._2).toList)
@@ -2139,73 +2133,78 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
override def getSoftwareByNode(nodeIds: Set[NodeId], status: InventoryStatus): IOResult[Map[NodeId, Seq[Software]]] = {
for {
- inventories <- nodeInfoService.getAllInventories(status)
- softwares <- softRef.get
+ facts <- nodeFactRepo.slowGetAllCompat(status, SelectFacts.softwareOnly).runCollect
} yield {
- inventories.collect {
- case (id, inv) if (nodeIds.contains(id)) =>
+ (facts.collect {
+ case f if (nodeIds.contains(f.id)) =>
(
- id,
- softwares.collect {
- case (k, s) if (inv.node.softwareIds.contains(k)) => s
- }.toList
+ f.id,
+ f.software.map(_.toSoftware)
)
- }
+ }).toMap
}
}
override def getAllSoftwareIds(): IOResult[Set[SoftwareUuid]] = softRef.get.map(_.keySet)
override def getSoftwaresForAllNodes(): IOResult[Set[SoftwareUuid]] = {
- nodeInfoService.getAllInventories(AcceptedInventory).map(_.flatMap(_._2.node.softwareIds).toSet)
+ ???
}
def getNodesbySofwareName(softName: String): IOResult[List[(NodeId, Software)]] = {
for {
- inventories <- nodeInfoService.getAllInventories(AcceptedInventory)
- softwares <- softRef.get
+ facts <- nodeFactRepo.slowGetAllCompat(AcceptedInventory, SelectFacts.softwareOnly).runCollect
} yield {
-
- inventories.toList.flatMap {
- case (id, inv) =>
- softwares.collect {
- case (k, s) if (s.name.exists(_ == softName) && inv.node.softwareIds.contains(k)) => (id, s)
- }
-
- }
+ facts.collect {
+ case f if (f.software.map(_.name).contains(f.id)) =>
+ (
+ f.id,
+ f.software
+ .find(_.name == softName)
+ .getOrElse(throw new IllegalArgumentException("for test - we just check it's here"))
+ .toSoftware
+ )
+ }.toList
}
}
}
+ val getNodesbySofwareName = new SoftDaoGetNodesbySofwareName(softwareDao)
+
+ val nodeFactRepo = CoreNodeFactRepository.make(nodeFactStorage, getNodesbySofwareName, Chunk()).runNow
+
object queryProcessor extends QueryProcessor {
+
import cats.implicits._
import com.normation.inventory.ldap.core.LDAPConstants._
import com.normation.rudder.domain.RudderLDAPConstants._
// return the value to corresponding to the given object/attribute
- def buildValues(objectName: String, attribute: String): PureResult[NodeDetails => List[String]] = {
+ def buildValues(objectName: String, attribute: String): PureResult[NodeFact => List[String]] = {
objectName match {
case OC_NODE =>
attribute match {
- case "OS" => Right((n: NodeDetails) => List(n.info.osDetails.os.name))
- case A_NODE_UUID => Right((n: NodeDetails) => List(n.info.id.value))
- case A_HOSTNAME => Right((n: NodeDetails) => List(n.info.hostname))
- case A_OS_NAME => Right((n: NodeDetails) => List(n.info.osDetails.os.name))
- case A_OS_FULL_NAME => Right((n: NodeDetails) => List(n.info.osDetails.fullName))
- case A_OS_VERSION => Right((n: NodeDetails) => List(n.info.osDetails.version.value))
- case A_OS_SERVICE_PACK => Right((n: NodeDetails) => n.info.osDetails.servicePack.toList)
- case A_OS_KERNEL_VERSION => Right((n: NodeDetails) => List(n.info.osDetails.kernelVersion.value))
- case A_ARCH => Right((n: NodeDetails) => n.info.archDescription.toList)
- case A_STATE => Right((n: NodeDetails) => List(n.info.state.name))
- case A_OS_RAM => Right((n: NodeDetails) => n.info.ram.map(_.size.toString).toList)
- case A_OS_SWAP => Right((n: NodeDetails) => n.nInv.swap.map(_.size.toString).toList)
- case A_AGENTS_NAME => Right((n: NodeDetails) => n.info.agentsName.map(_.agentType.id).toList)
- case A_ACCOUNT => Right((n: NodeDetails) => n.nInv.accounts.toList)
- case A_LIST_OF_IP => Right((n: NodeDetails) => n.info.ips)
- case A_ROOT_USER => Right((n: NodeDetails) => List(n.info.localAdministratorAccountName))
+ case "OS" => Right((n: NodeFact) => List(n.os.os.name))
+ case A_NODE_UUID => Right((n: NodeFact) => List(n.id.value))
+ case A_HOSTNAME => Right((n: NodeFact) => List(n.fqdn))
+ case A_OS_NAME => Right((n: NodeFact) => List(n.os.os.name))
+ case A_OS_FULL_NAME => Right((n: NodeFact) => List(n.os.fullName))
+ case A_OS_VERSION => Right((n: NodeFact) => List(n.os.version.value))
+ case A_OS_SERVICE_PACK => Right((n: NodeFact) => n.os.servicePack.toList)
+ case A_OS_KERNEL_VERSION => Right((n: NodeFact) => List(n.os.kernelVersion.value))
+ case A_ARCH => Right((n: NodeFact) => n.archDescription.toList)
+ case A_STATE => Right((n: NodeFact) => List(n.rudderSettings.state.name))
+ case A_OS_RAM => Right((n: NodeFact) => n.ram.map(_.size.toString).toList)
+ case A_OS_SWAP => Right((n: NodeFact) => n.swap.map(_.size.toString).toList)
+ case A_AGENTS_NAME => Right((n: NodeFact) => List(n.rudderAgent.agentType.id))
+ case A_ACCOUNT => Right((n: NodeFact) => n.accounts.toList)
+ case A_LIST_OF_IP => Right((n: NodeFact) => n.ipAddresses.map(_.inet).toList)
+ case A_ROOT_USER => Right((n: NodeFact) => List(n.rudderAgent.user))
case A_INVENTORY_DATE =>
- Right((n: NodeDetails) => List(n.info.inventoryDate.toString(ISODateTimeFormat.dateTimeNoMillis())))
- case A_POLICY_SERVER_UUID => Right((n: NodeDetails) => List(n.info.policyServerId.value))
+ Right((n: NodeFact) =>
+ List(n.lastInventoryDate.getOrElse(n.factProcessedDate).toString(ISODateTimeFormat.dateTimeNoMillis()))
+ )
+ case A_POLICY_SERVER_UUID => Right((n: NodeFact) => List(n.rudderSettings.policyServerId.value))
case _ => Left(Inconsistency(s"object '${objectName}' doesn't have attribute '${attribute}'"))
}
case x => Left(Unexpected(s"Case value '${x}' for query processor not yet implemented in test, see `MockServices.scala`"))
@@ -2229,7 +2228,7 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
}
}
- def filterForLine(line: CriterionLine, nodes: List[NodeDetails]): PureResult[List[NodeDetails]] = {
+ def filterForLine(line: CriterionLine, nodes: List[NodeFact]): PureResult[List[NodeFact]] = {
for {
values <- buildValues(line.objectType.objectType, line.attribute.name)
matching <- nodes.traverse(n => compare(values(n), line.comparator, line.value).map(if (_) Some(n) else None))
@@ -2241,8 +2240,8 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
def filterForLines(
lines: List[CriterionLine],
combine: CriterionComposition,
- nodes: List[NodeDetails]
- ): PureResult[List[NodeDetails]] = {
+ nodes: List[NodeFact]
+ ): PureResult[List[NodeFact]] = {
for {
byLine <- lines.traverse(filterForLine(_, nodes))
} yield {
@@ -2257,58 +2256,48 @@ z5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==
override def process(query: Query): Box[Seq[NodeId]] = {
for {
- nodes <- nodeInfoService.nodeBase.get
+ nodes <- nodeFactStorage.nodeFactBase.get
matching <- filterForLines(query.criteria, query.composition, nodes.map(_._2).toList).toIO
} yield {
- matching.map(_.info.id).toSeq
+ matching.map(_.id).toSeq
}
}.toBox
override def processOnlyId(query: Query): Box[Seq[NodeId]] = process(query).map(_.toSeq)
}
+ val nodeInfoService = new NodeInfoServiceProxy(nodeFactRepo)
+ val fullInventoryRepository = new NodeFactFullInventoryRepositoryProxy(nodeFactRepo)
+ val woNodeRepository = new WoFactNodeRepositoryProxy(nodeFactRepo)
+
object newNodeManager extends NewNodeManager {
- def nodeToSrv(n: NodeDetails): Srv = {
- Srv(
- n.nInv.main.id,
- n.nInv.main.status,
- n.nInv.main.hostname,
- n.nInv.main.osDetails.os.kernelName,
- n.nInv.main.osDetails.os.name,
- n.nInv.main.osDetails.fullName,
- n.info.ips,
- n.info.creationDate,
- n.info.isPolicyServer
- )
+ val list = new FactListNewNodes(nodeFactRepo)
+
+ override def listNewNodes: IOResult[Seq[CoreNodeFact]] = list.listNewNodes
+
+ override def accept(id: NodeId)(implicit cc: ChangeContext): IOResult[CoreNodeFact] = {
+ nodeFactRepo.changeStatus(id, AcceptedInventory) *>
+ nodeFactRepo
+ .getCompat(id, AcceptedInventory)
+ .notOptional(s"Accepted node '${id.value}' is missing")
}
- override def listNewNodes: Box[Seq[Srv]] = {
- for {
- nodes <- nodeInfoService.nodeBase.get
- pending = nodes.toList.collect {
- case (id, n @ NodeDetails(info, nInv, mInv)) if (nInv.main.status == PendingInventory) => nodeToSrv(n)
- }
- } yield pending
- }.toBox
- override def accept(id: NodeId, modId: ModificationId, actor: EventActor): Box[FullInventory] = {
- (nodeInfoService.nodeBase.modifyZIO { nodes =>
- nodes.get(id) match {
- case None => Inconsistency(s"node is missing").fail
- case Some(x) =>
- import com.softwaremill.quicklens._
- val xx = x.modify(_.nInv.main.status).setTo(AcceptedInventory)
- (FullInventory(xx.nInv, xx.mInv), nodes + ((id, xx))).succeed
+ override def refuse(id: NodeId)(implicit cc: ChangeContext): IOResult[CoreNodeFact] = {
+ nodeFactRepo
+ .getCompat(id, PendingInventory)
+ .flatMap {
+ case None => Inconsistency(s"node not found").fail
+ case Some(x) => nodeFactRepo.delete(id) *> x.succeed
}
- }).toBox
}
- override def refuse(id: NodeId, modId: ModificationId, actor: EventActor): Box[Srv] = ???
-
- override def accept(ids: Seq[NodeId], modId: ModificationId, actor: EventActor, actorIp: String): Box[Seq[FullInventory]] =
- ???
-
- override def refuse(id: Seq[NodeId], modId: ModificationId, actor: EventActor, actorIp: String): Box[Seq[Srv]] = ???
+ override def acceptAll(ids: Seq[NodeId])(implicit cc: ChangeContext): IOResult[Seq[CoreNodeFact]] = {
+ ZIO.foreach(ids)(accept)
+ }
+ override def refuseAll(ids: Seq[NodeId])(implicit cc: ChangeContext): IOResult[Seq[CoreNodeFact]] = {
+ ZIO.foreach(ids)(refuse)
+ }
}
}
@@ -2349,12 +2338,12 @@ class MockNodeGroups(nodesRepo: MockNodes) {
override def getGroupsByCategory(
includeSystem: Boolean
- ): IOResult[immutable.SortedMap[List[NodeGroupCategoryId], CategoryAndNodeGroup]] = {
+ ): IOResult[ISortedMap[List[NodeGroupCategoryId], CategoryAndNodeGroup]] = {
def getChildren(
parents: List[NodeGroupCategoryId],
root: FullNodeGroupCategory
- ): immutable.SortedMap[List[NodeGroupCategoryId], CategoryAndNodeGroup] = {
- val c = immutable.SortedMap(
+ ): ISortedMap[List[NodeGroupCategoryId], CategoryAndNodeGroup] = {
+ val c = ISortedMap(
(root.id :: parents, CategoryAndNodeGroup(root.toNodeGroupCategory, root.ownGroups.values.map(_.nodeGroup).toSet))
)
root.subCategories.foldLeft(c) { case (current, n) => current ++ getChildren(root.id :: parents, n) }
@@ -2369,7 +2358,7 @@ class MockNodeGroups(nodesRepo: MockNodes) {
c
}
- override def getCategoryHierarchy: IOResult[immutable.SortedMap[List[NodeGroupCategoryId], NodeGroupCategory]] = {
+ override def getCategoryHierarchy: IOResult[ISortedMap[List[NodeGroupCategoryId], NodeGroupCategory]] = {
getGroupsByCategory(true).map(
_.map { case (k, v) => (k, v.category) }
)
@@ -2724,7 +2713,7 @@ class MockNodeGroups(nodesRepo: MockNodes) {
g0props,
None,
false,
- Set(nodesRepo.rootId, nodesRepo.node1.id, nodesRepo.node2.id),
+ Set(MockNodes.rootId, MockNodes.node1.id, MockNodes.node2.id),
true
)
val g1 =
@@ -2746,7 +2735,7 @@ class MockNodeGroups(nodesRepo: MockNodes) {
Nil,
None,
false,
- nodesRepo.nodeIds.filter(_.value.toInt % 2 == 0),
+ MockNodes.nodeIds.filter(_.value.toInt % 2 == 0),
true
)
val g4 = NodeGroup(
@@ -2756,7 +2745,7 @@ class MockNodeGroups(nodesRepo: MockNodes) {
Nil,
None,
false,
- nodesRepo.nodeIds.filter(_.value.toInt % 2 != 0),
+ MockNodes.nodeIds.filter(_.value.toInt % 2 != 0),
true
)
val g5 = NodeGroup(
@@ -2766,7 +2755,7 @@ class MockNodeGroups(nodesRepo: MockNodes) {
Nil,
None,
false,
- nodesRepo.nodeIds.filter(_.value.toInt % 3 == 0),
+ MockNodes.nodeIds.filter(_.value.toInt % 3 == 0),
true
)
val g6 = NodeGroup(
@@ -2776,7 +2765,7 @@ class MockNodeGroups(nodesRepo: MockNodes) {
Nil,
None,
false,
- nodesRepo.nodeIds.filter(_.value.toInt % 5 == 0),
+ MockNodes.nodeIds.filter(_.value.toInt % 5 == 0),
true
)
val groups = Set(g0, g1, g2, g3, g4, g5, g6).map(g => (g.id, g))
@@ -2886,8 +2875,9 @@ class MockLdapQueryParsing(mockGit: MockGitConfigRepo, mockNodeGroups: MockNodeG
val nodeDit = new NodeDit(LDAP_BASEDN)
val inventoryDitService: InventoryDitService =
new InventoryDitServiceImpl(pendingNodesDitImpl, acceptedNodesDitImpl, removedNodesDitImpl)
- val getSubGroupChoices = () => mockNodeGroups.groupsRepo.getAll().map(seq => seq.map(g => SubGroupChoice(g.id, g.name)))
- val ditQueryDataImpl = new DitQueryData(acceptedNodesDitImpl, nodeDit, rudderDit, getSubGroupChoices)
+ val getSubGroupChoices = new DefaultSubGroupComparatorRepository(mockNodeGroups.groupsRepo)
+ val nodeQueryData = new NodeQueryCriteriaData(() => getSubGroupChoices)
+ val ditQueryDataImpl = new DitQueryData(acceptedNodesDitImpl, nodeDit, rudderDit, nodeQueryData)
val queryParser = new CmdbQueryParser with DefaultStringQueryParser with JsonQueryLexer {
override val criterionObjects = Map[String, ObjectCriterion]() ++ ditQueryDataImpl.criteriaMap
}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/MockLdapFactStorage.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/MockLdapFactStorage.scala
new file mode 100644
index 00000000000..f33f47db632
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/MockLdapFactStorage.scala
@@ -0,0 +1,150 @@
+/*
+ *************************************************************************************
+ * Copyright 2023 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.facts.nodes
+
+import better.files._
+import com.normation.inventory.ldap.core.FullInventoryRepositoryImpl
+import com.normation.inventory.ldap.core.InventoryDit
+import com.normation.inventory.ldap.core.InventoryDitService
+import com.normation.inventory.ldap.core.InventoryDitServiceImpl
+import com.normation.inventory.ldap.core.InventoryMapper
+import com.normation.inventory.ldap.core.ReadOnlySoftwareDAOImpl
+import com.normation.inventory.ldap.provisioning._
+import com.normation.ldap.ldif.DefaultLDIFFileLogger
+import com.normation.ldap.listener.InMemoryDsConnectionProvider
+import com.normation.ldap.sdk.LDAPConnectionProvider
+import com.normation.ldap.sdk.RoLDAPConnection
+import com.normation.ldap.sdk.RwLDAPConnection
+import com.normation.rudder.domain.NodeDit
+import com.normation.rudder.domain.RudderDit
+import com.normation.rudder.repository.ldap.LDAPEntityMapper
+import com.normation.rudder.repository.ldap.ZioTReentrantLock
+import com.normation.utils.StringUuidGeneratorImpl
+import com.unboundid.ldap.sdk.DN
+
+object MockLdapFactStorage {
+
+ val tmp = File.newTemporaryDirectory("rudder-test-ldap-schema-files-")
+ tmp.deleteOnExit(true)
+
+ val ldifLogger = new DefaultLDIFFileLogger("TestQueryProcessor", "/tmp/normation/rudder/ldif")
+
+ // init of in memory LDAP directory
+ val schemaLDIFs = {
+ val schemaDir = tmp / "schema"
+ schemaDir.createDirectories()
+
+ (
+ "00-core" ::
+ "01-pwpolicy" ::
+ "04-rfc2307bis" ::
+ "05-rfc4876" ::
+ "099-0-inventory" ::
+ "099-1-rudder" ::
+ Nil
+ ) map { name =>
+ // toURI is needed for https://issues.rudder.io/issues/19186
+ val dest = schemaDir / s"${name}.ldif"
+ dest.writeText(Resource.getAsString(s"ldap-data/schema/${name}.ldif"))
+ dest.pathAsString
+ }
+ }
+ val bootstrapLDIFs = {
+ val bootstrapDir = tmp / "bootstrap"
+ bootstrapDir.createDirectories()
+
+ ("ldap/bootstrap.ldif" :: "ldap-data/inventory-sample-data.ldif" :: Nil) map { name =>
+ // toURI is needed for https://issues.rudder.io/issues/19186
+ val dest = bootstrapDir / name.split("/")(1)
+ dest.writeText(Resource.getAsString(name))
+ dest.pathAsString
+ }
+ }
+
+ val ldap = InMemoryDsConnectionProvider[RwLDAPConnection](
+ baseDNs = "cn=rudder-configuration" :: Nil,
+ schemaLDIFPaths = schemaLDIFs,
+ bootstrapLDIFPaths = bootstrapLDIFs,
+ ldifLogger
+ )
+
+ // for easier access in tests
+ def testServer = ldap.server
+
+ // close your eyes for next line
+ val ldapRo = ldap.asInstanceOf[LDAPConnectionProvider[RoLDAPConnection]]
+
+ val acceptedDIT = new InventoryDit(
+ new DN("ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration"),
+ new DN("ou=Inventories,cn=rudder-configuration"),
+ "test"
+ )
+
+ val removedDIT = new InventoryDit(
+ new DN("ou=Removed Inventories,ou=Inventories,cn=rudder-configuration"),
+ new DN("ou=Inventories,cn=rudder-configuration"),
+ "test"
+ )
+ val pendingDIT = new InventoryDit(
+ new DN("ou=Pending Inventories,ou=Inventories,cn=rudder-configuration"),
+ new DN("ou=Inventories,cn=rudder-configuration"),
+ "test"
+ )
+ val nodeDit = new NodeDit(new DN("cn=rudder-configuration"))
+ val rudderDit = new RudderDit(new DN("ou=Rudder, cn=rudder-configuration"))
+ val inventoryDitService: InventoryDitService = new InventoryDitServiceImpl(pendingDIT, acceptedDIT, removedDIT)
+ val inventoryMapper = new InventoryMapper(inventoryDitService, pendingDIT, acceptedDIT, removedDIT)
+ val ldapMapper = new LDAPEntityMapper(rudderDit, nodeDit, acceptedDIT, null, inventoryMapper)
+ val ldapFullInventoryRepository = new FullInventoryRepositoryImpl(inventoryDitService, inventoryMapper, ldap)
+ val softwareGet = new ReadOnlySoftwareDAOImpl(inventoryDitService, ldapRo, inventoryMapper)
+ val softwareSave = new NameAndVersionIdFinder("check_name_and_version", ldapRo, inventoryMapper, acceptedDIT)
+
+ val nodeFactStorage = new LdapNodeFactStorage(
+ ldap,
+ nodeDit,
+ inventoryDitService,
+ ldapMapper,
+ inventoryMapper,
+ new ZioTReentrantLock("node-lock"),
+ ldapFullInventoryRepository,
+ softwareGet,
+ softwareSave,
+ new StringUuidGeneratorImpl()
+ )
+
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestCoreNodeFactInventory.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestCoreNodeFactInventory.scala
new file mode 100644
index 00000000000..1e95f352f05
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestCoreNodeFactInventory.scala
@@ -0,0 +1,421 @@
+/*
+ *************************************************************************************
+ * Copyright 2023 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.facts.nodes
+
+import better.files._
+import com.normation.errors._
+import com.normation.eventlog.EventActor
+import com.normation.eventlog.ModificationId
+import com.normation.inventory.domain._
+import com.normation.inventory.ldap.core.InventoryDit
+import com.normation.inventory.ldap.core.ReadOnlySoftwareDAOImpl
+import com.normation.rudder.domain.Constants
+import com.normation.rudder.domain.nodes.MachineInfo
+import com.normation.utils.DateFormaterService
+import com.normation.zio._
+import com.normation.zio.ZioRuntime
+import com.softwaremill.quicklens._
+import com.unboundid.ldap.sdk.SearchScope
+import java.security.Security
+import org.apache.commons.io.FileUtils
+import org.bouncycastle.jce.provider.BouncyCastleProvider
+import org.joda.time.DateTime
+import org.junit.runner._
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.specs2.specification.BeforeAfterAll
+import scala.annotation.nowarn
+import zio._
+
+/**
+ *
+ * Test the processing of new inventory:
+ * - check that new, never seen nodes end into pending
+ * - check that new, already pending nodes update pending
+ * - check that accepted nodes are updated
+ * - check that signature things work.
+ *
+ * That test does not check for the file observer, only save logic.
+ */
+@RunWith(classOf[JUnitRunner])
+@nowarn("msg=a type was inferred to be `\\w+`; this may indicate a programming error.")
+class TestCoreNodeFactInventory extends Specification with BeforeAfterAll {
+
+ // load bouncyCastle
+ Security.addProvider(new BouncyCastleProvider())
+
+ def nodeExists(id: String, dit: InventoryDit): Boolean = {
+ MockLdapFactStorage.ldap.server.entryExists(dit.NODES.NODE.dn(id).toString)
+ }
+
+ def nodeAsString(id: String, dit: InventoryDit): String = {
+ val sb = new java.lang.StringBuilder()
+ MockLdapFactStorage.ldap.server.getEntry(dit.NODES.NODE.dn(id).toString).toString(sb)
+ sb.toString()
+ }
+
+ // a fact storage that keeps a trace of all call to it, so that we can debug/set expectation
+ object factStorage extends NodeFactStorage {
+ val backend = MockLdapFactStorage.nodeFactStorage
+ val callStack = Ref.make(List.empty[String]).runNow
+
+ def clearCallStack: Unit = callStack.set(Nil).runNow
+
+ override def save(nodeFact: NodeFact)(implicit attrs: SelectFacts = SelectFacts.all): IOResult[StorageChangeEventSave] = {
+ for {
+ _ <- callStack.update(s"save ${nodeFact.id}" :: _)
+ r <- backend.save(nodeFact)
+ } yield r
+ }
+
+ override def changeStatus(nodeId: NodeId, status: InventoryStatus): IOResult[StorageChangeEventStatus] = {
+ for {
+ _ <- callStack.update(s"changeStatus ${nodeId} to ${status.name}" :: _)
+ r <- backend.changeStatus(nodeId, status)
+ } yield r
+ }
+
+ override def delete(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[StorageChangeEventDelete] = {
+ for {
+ _ <- callStack.update(s"delete ${nodeId}" :: _)
+ r <- backend.delete(nodeId)
+ } yield r
+ }
+
+ override def getPending(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ for {
+ _ <- callStack.update(s"getPending ${nodeId}" :: _)
+ r <- backend.getPending(nodeId)
+ } yield r
+ }
+
+ override def getAccepted(nodeId: NodeId)(implicit attrs: SelectFacts): IOResult[Option[NodeFact]] = {
+ for {
+ _ <- callStack.update(s"getAccepted ${nodeId}" :: _)
+ r <- backend.getAccepted(nodeId)
+ } yield r
+ }
+
+ override def getAllPending()(implicit attrs: SelectFacts): IOStream[NodeFact] = {
+ callStack.update(s"getAllPending" :: _).runNow // gosh, it's for test only
+ backend.getAllPending()
+ }
+
+ override def getAllAccepted()(implicit attrs: SelectFacts): IOStream[NodeFact] = {
+ callStack.update(s"getAllAccepted" :: _).runNow // gosh, it's for test only
+ backend.getAllAccepted()
+ }
+ }
+
+ implicit class RunThing[E, T](thing: ZIO[Any, E, T]) {
+ def testRun = ZioRuntime.unsafeRun(thing.either)
+ }
+
+ implicit class RunOptThing[A](thing: IOResult[Option[A]]) {
+ def testRunGet: A = ZioRuntime.unsafeRun(thing.either) match {
+ case Right(Some(a)) => a
+ case Right(None) => throw new RuntimeException(s"Error in test: found None, expected Some")
+ case Left(err) => throw new RuntimeException(s"Error in test: ${err}")
+ }
+ }
+
+ implicit class TestIsOK[E, T](thing: ZIO[Any, E, T]) {
+ def isOK = thing.testRun must beRight
+ }
+
+ implicit class ForceGetE[E, A](opt: Either[E, A]) {
+ def forceGet: A = opt match {
+ case Right(x) => x
+ case Left(err) => throw new Exception(s"error in Test: ${err}")
+ }
+ }
+
+ implicit def stringToNodeId(id: String): NodeId = NodeId(id)
+
+ val basePath = s"/tmp/test-rudder-nodefact/${DateFormaterService.gitTagFormat.print(DateTime.now())}"
+
+ override def beforeAll(): Unit = {}
+
+ override def afterAll(): Unit = {
+ if (java.lang.System.getProperty("tests.clean.tmp") != "false") {
+ FileUtils.deleteDirectory(File(basePath).toJava)
+ }
+ }
+
+ val callbackLog = Ref.make(Chunk.empty[NodeFactChangeEvent]).runNow
+ def resetLog = callbackLog.set(Chunk.empty).runNow
+ def getLogName = callbackLog.get.map(_.map(_.name)).runNow
+
+ val nodeBySoftwareName = new SoftDaoGetNodesbySofwareName(
+ new ReadOnlySoftwareDAOImpl(
+ MockLdapFactStorage.inventoryDitService,
+ MockLdapFactStorage.ldapRo,
+ MockLdapFactStorage.inventoryMapper
+ )
+ )
+
+ val factRepo = {
+ val trailCB = CoreNodeFactChangeEventCallback("trail", e => callbackLog.update(_.appended(e.event)))
+// val logCB = CoreNodeFactChangeEventCallback("log", e => effectUioUnit(println(s"**** ${e.name}"))))
+ CoreNodeFactRepository.make(factStorage, nodeBySoftwareName, Chunk(trailCB)).runNow
+ }
+
+// org.slf4j.LoggerFactory
+// .getLogger("inventory-processing")
+// .asInstanceOf[ch.qos.logback.classic.Logger]
+// .setLevel(ch.qos.logback.classic.Level.TRACE)
+
+ sequential
+
+ // node7 has the following inventory info:
+ // - one software
+ // - one mount point
+ // - machine2 (physical)
+ // - a bios
+ val node7id = NodeId("node7")
+ val machineId = MachineUuid("machine2")
+
+ implicit val cc: ChangeContext = ChangeContext.newForRudder()
+
+ // things that are empty on node7 because not defined
+ def node7UndefinedElements(n: NodeFact) = List(
+ Chunk.fromIterable(n.swap),
+ n.accounts,
+ n.controllers,
+ n.environmentVariables,
+ n.inputs,
+ n.localUsers,
+ n.localUsers,
+ n.logicalVolumes,
+ n.memories,
+ n.networks,
+ n.physicalVolumes,
+ n.ports,
+ n.processes,
+ n.processors,
+ n.slots,
+ n.softwareUpdate,
+ n.sounds,
+ n.storages,
+ n.videos,
+ n.vms
+ )
+
+ implicit val testChangeContext: ChangeContext =
+ ChangeContext(ModificationId("test-mod-id"), EventActor("test"), DateTime.now(), None, None)
+
+ "query action" should {
+
+ "allow to get the whole node fact, included inventory and software" in {
+
+ implicit val attrs = SelectFacts.all
+
+ factStorage.clearCallStack
+ val node = factRepo.slowGet(node7id).notOptional("node7 must be here").runNow
+
+ (factStorage.callStack.get.runNow.size === 1) and
+ (node.bios.size === 1) and
+ (
+ node.bios.head === Bios(
+ "bios1",
+ None,
+ Some(new Version("6.00")),
+ Some(SoftwareEditor("Phoenix Technologies LTD"))
+ )
+ ) and
+ (node.machine === MachineInfo(machineId, PhysicalMachineType, None, None)) and
+ (node.fileSystems.size === 1) and
+ (
+ node.fileSystems.head === FileSystem(
+ "/",
+ Some("ext3"),
+ None,
+ None,
+ Some(MemorySize(10L)),
+ Some(MemorySize(803838361699L))
+ )
+ ) and
+ (node.software.size === 1) and
+ (node.software.head === SoftwareFact("Software 0", new Version("1.0.0"))) and
+ (node7UndefinedElements(node) must contain((x: Chunk[_]) => x must beEmpty).foreach)
+
+ }
+
+ "allow to get core node fact without touching LDAP" in {
+ factStorage.clearCallStack
+ val node7 = factRepo.get(node7id).notOptional("node7 must be there").runNow
+
+ (node7.fqdn === "node7.normation.com") and
+ (factStorage.callStack.get.runNow match {
+ case Nil => ok
+ case l => ko(s"Storage was call: ${l.mkString("\n", "\n", "")}")
+ })
+ }
+
+ "allow to get software without the other parts of inventory" in { // how to chek that we don't retrieve cpu filesystems etc?
+ factStorage.clearCallStack
+ implicit val attrs = SelectFacts.softwareOnly
+ val node = factRepo.slowGet(node7id).notOptional("node7 must be here").runNow
+
+ (factStorage.callStack.get.runNow.size === 1) and
+ (node.bios.size === 0) and
+ (node.machine === MachineInfo(machineId, PhysicalMachineType, None, None)) and // we always get that
+ (node.fileSystems.size === 0) and
+ (node.software.size === 1) and
+ (node.software.head === SoftwareFact("Software 0", new Version("1.0.0"))) and
+ (node7UndefinedElements(node) must contain((x: Chunk[_]) => x must beEmpty).foreach)
+ }
+
+ // for now, we can't selectively choose what sub element of inventory we retrieve. It could be done if needed
+ // for better perf. The use case could be: api requesting only CPU on all nodes or that kind of things.
+ "allow to get only a sub-set of inventory without fetching software" in {
+ factStorage.clearCallStack
+ implicit val attrs = SelectFacts.none.modify(_.bios).using(_.toRetrieve)
+ val node = factRepo.slowGet(node7id).notOptional("node7 must be here").runNow
+
+ (factStorage.callStack.get.runNow.size === 1) and
+ (node.bios.size === 1) and
+ (
+ node.bios.head === Bios(
+ "bios1",
+ None,
+ Some(new Version("6.00")),
+ Some(SoftwareEditor("Phoenix Technologies LTD"))
+ )
+ ) and
+ (node.fileSystems.size === 0) and
+ (node.software.size === 0) and
+ (node7UndefinedElements(node) must contain((x: Chunk[_]) => x must beEmpty).foreach)
+ }
+ }
+
+ "basic update action" should {
+ "we can save a whole inventory and changing everything in storage, included software" >> {
+ factStorage.clearCallStack
+ val node = factRepo.slowGet(node7id)(SelectNodeStatus.Accepted, SelectFacts.all).notOptional("node7 must be here").runNow
+
+ val updated = node
+ .modify(_.software)
+ .using(_.appended(SoftwareFact("s2", new Version("1.2"))))
+ .modify(_.environmentVariables)
+ .using(_.appended(("envVAR", "envVALUE")))
+ .modify(_.networks)
+ .using(_.appended(Network("eth0")))
+ .modify(_.slots)
+ .using(_.appended(Slot("slot0")))
+
+ factRepo.save(updated)(testChangeContext, SelectFacts.all).runNow
+
+ // check that ldap entries where modified
+ (MockLdapFactStorage.testServer
+ .getEntry("nodeId=node7,ou=Nodes,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration")
+ .getAttributeValue("environmentVariable") must beEqualTo("""{"name":"envVAR","value":"envVALUE"}""")) and
+ (MockLdapFactStorage.testServer.entryExists(
+ "networkInterface=eth0,nodeId=node7,ou=Nodes,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration"
+ ) must beTrue) and
+ (MockLdapFactStorage.testServer.entryExists(
+ "portName=slot0,machineId=machine2,ou=Machines,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration"
+ ) must beTrue) and
+ (
+ MockLdapFactStorage.testServer
+ .search("ou=Software,ou=Inventories,cn=rudder-configuration", SearchScope.ONE, "(cn=s2)")
+ .getSearchEntries
+ .size()
+ must beEqualTo(1)
+ )
+ }
+
+ "we can change only one inventory aspect without touching others even if they are not the same in our business object" >> {
+ factStorage.clearCallStack
+ val node = factRepo
+ .slowGet(node7id)(
+ SelectNodeStatus.Accepted,
+ SelectFacts.all
+ )
+ .notOptional("node7 must be here")
+ .runNow
+
+ val updated = node
+ .modify(_.software)
+ .using(_.appended(SoftwareFact("s3", new Version("1.3"))))
+ .modify(_.environmentVariables)
+ .using(_.appended(("bad", "bad")))
+ .modify(_.networks)
+ .using(_.appended(Network("eth1")))
+ .modify(_.slots)
+ .using(_.appended(Slot("slot1")))
+
+ factRepo.save(updated)(testChangeContext, SelectFacts.none.modify(_.networks).using(_.toRetrieve)).runNow
+
+ // check that ONLY network ldap entry was modified
+ (MockLdapFactStorage.testServer
+ .getEntry("nodeId=node7,ou=Nodes,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration")
+ .getAttribute("environmentVariable")
+ .hasValue("""{"name":"bad","value":"bad"}""") must beFalse) and
+ (
+ MockLdapFactStorage.testServer.entryExists(
+ "networkInterface=eth1,nodeId=node7,ou=Nodes,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration"
+ ) must beTrue
+ ) and
+ (
+ MockLdapFactStorage.testServer.entryExists(
+ "portName=slot1,machineId=machine2,ou=Machines,ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration"
+ ) must beFalse
+ ) and
+ (
+ MockLdapFactStorage.testServer
+ .search("ou=Software,ou=Inventories,cn=rudder-configuration", SearchScope.ONE, "(cn=s3)")
+ .getSearchEntries
+ .size()
+ must beEqualTo(0)
+ )
+ }
+
+ "root status can not be modified" >> {
+ val res = (for {
+ r <- factRepo.get(Constants.ROOT_POLICY_SERVER_ID).notOptional("root must be here")
+ _ <- factRepo.save(NodeFact.fromMinimal(r.modify(_.rudderSettings.status).setTo(PendingInventory)))(
+ testChangeContext,
+ SelectFacts.none
+ )
+ } yield ()).either.runNow
+
+ res must beLeft
+ }
+ }
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestFullInventoryRepoProxy.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestFullInventoryRepoProxy.scala
new file mode 100644
index 00000000000..22ab6e649be
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestFullInventoryRepoProxy.scala
@@ -0,0 +1,394 @@
+/*
+ *************************************************************************************
+ * Copyright 2023 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.facts.nodes
+
+import com.normation.errors._
+import com.normation.inventory.domain._
+import com.normation.inventory.services.core.FullInventoryRepository
+import com.normation.zio._
+import com.normation.zio.ZioRuntime
+import com.softwaremill.quicklens._
+import org.joda.time.DateTime
+import org.junit.runner._
+import org.specs2.mutable._
+import org.specs2.runner._
+import zio._
+import zio.concurrent.ReentrantLock
+import zio.syntax._
+
+final case class SystemError(cause: Throwable) extends RudderError {
+ def msg = "Error in test"
+}
+
+/*
+ * A class to trace semantic changes between full ldap repo with ldap
+ * and with fact backend (see com/normation/inventory/ldap/core/TestInventory.scala
+ * for original results)
+ */
+//@silent("a type was inferred to be `\\w+`; this may indicate a programming error.")
+@RunWith(classOf[JUnitRunner])
+class TestInventory extends Specification {
+
+ implicit class RunThing[E, T](thing: ZIO[Any, E, T]) {
+ def testRun = ZioRuntime.unsafeRun(thing.either)
+ }
+ implicit class RunOptThing[A](thing: IOResult[Option[A]]) {
+ def testRunGet: A = ZioRuntime.unsafeRun(thing.either) match {
+ case Right(Some(a)) => a
+ case Right(None) => throw new RuntimeException(s"Error in test: found None, expected Some")
+ case Left(err) => throw new RuntimeException(s"Error in test: ${err}")
+ }
+ }
+
+ implicit class TestIsOK[E, T](thing: ZIO[Any, E, T]) {
+ def isOK = thing.testRun must beRight
+ }
+
+ implicit class ForceGetE[E, A](opt: Either[E, A]) {
+ def forceGet: A = opt match {
+ case Right(x) => x
+ case Left(err) => throw new Exception(s"error in Test: ${err}")
+ }
+ }
+
+ // TODO WARNING POC: this can't work on a machine with lots of node
+ val callbackLog = Ref.make(Chunk.empty[NodeFactChangeEvent]).runNow
+
+ def resetLog = callbackLog.set(Chunk.empty).runNow
+
+ def getLogName = callbackLog.get.map(_.map(_.name)).runNow
+
+ val pendingRef = (Ref.make(Map[NodeId, CoreNodeFact]())).runNow
+ val acceptedRef = (Ref.make(Map[NodeId, CoreNodeFact]())).runNow
+
+ def resetStorage = (for {
+ _ <- pendingRef.set(Map())
+ _ <- acceptedRef.set(Map())
+ } yield ()).runNow
+
+ object noopNodeBySoftwareName extends GetNodesbySofwareName {
+ override def apply(softName: String): IOResult[List[(NodeId, Software)]] = {
+ Nil.succeed
+ }
+ }
+
+ val factRepo = {
+ for {
+ callbacks <- Ref.make(Chunk.empty[NodeFactChangeEventCallback])
+ lock <- ReentrantLock.make()
+ r = new CoreNodeFactRepository(NoopFactStorage, noopNodeBySoftwareName, pendingRef, acceptedRef, callbacks, lock)
+ _ <- r.registerChangeCallbackAction(CoreNodeFactChangeEventCallback("trail", e => callbackLog.update(_.appended(e.event))))
+ // _ <- r.registerChangeCallbackAction(new NodeFactChangeEventCallback("log", e => effectUioUnit(println(s"**** ${e.name}"))))
+ } yield {
+ r
+ }
+ }.runNow
+ // needed because the in memory LDAP server is not used with connection pool
+ sequential
+
+ val allStatus = Seq(RemovedInventory, PendingInventory, AcceptedInventory)
+
+ // shortcut to create a machine with the name has ID in the given status
+ def machine(name: String, status: InventoryStatus) = MachineInventory(
+ MachineUuid(name),
+ status,
+ PhysicalMachineType,
+ None,
+ None,
+ Some(DateTime.parse("2023-01-11T10:20:30.000Z")), // now, node and inventory always have the same
+ Some(DateTime.parse("2023-02-22T15:25:35.000Z")), // inventory and received date
+ Some(Manufacturer("manufacturer")),
+ None,
+ Nil,
+ Nil,
+ Nil,
+ Nil,
+ Nil,
+ Nil,
+ Nil,
+ Nil,
+ Nil
+ )
+ // shortcut to create a node with the name has ID and the given machine, in the
+ // given status, has container.
+ def node(name: String, status: InventoryStatus, container: (MachineUuid, InventoryStatus)) = NodeInventory(
+ NodeSummary(
+ NodeId(name),
+ status,
+ "root",
+ "localhost",
+ Linux(
+ Debian,
+ "foo",
+ new Version("1.0"),
+ None,
+ new Version("1.0")
+ ),
+ NodeId("root"),
+ CertifiedKey
+ ),
+ inventoryDate = Some(DateTime.parse("2023-01-11T10:20:30.000Z")),
+ receiveDate = Some(DateTime.parse("2023-02-22T15:25:35.000Z")),
+ agents = Seq(NodeFact.defaultRudderAgent("root").toAgentInfo), // always present now
+ machineId = Some(container)
+ )
+
+ def full(n: NodeInventory, m: MachineInventory) = FullInventory(n, Some(m))
+
+ val repo: FullInventoryRepository[Unit] = new NodeFactFullInventoryRepositoryProxy(factRepo)
+
+ "Saving, finding and moving node" should {
+
+ "Save node for machine, whatever the presence or status of the machine" in {
+ resetStorage
+ val mid = MachineUuid("foo")
+
+ val n1 = node("acceptedNode", AcceptedInventory, (mid, AcceptedInventory))
+ val n2 = node("pendingNode", PendingInventory, (mid, AcceptedInventory))
+ val n3 = node("removedNode", RemovedInventory, (mid, AcceptedInventory))
+
+ (
+ repo.save(FullInventory(n1, None)).isOK
+ and repo.save(FullInventory(n2, None)).isOK
+ and repo.save(FullInventory(n3, None)).isOK
+ )
+ }
+
+ "find back the machine after a move" in {
+ resetStorage
+ val m = machine("findBackMachine", PendingInventory)
+ val n = node("findBackNode", PendingInventory, (m.id, m.status))
+
+ (
+ repo.save(full(n, m)).isOK
+ and repo.move(n.main.id, PendingInventory, AcceptedInventory).isOK
+ and {
+ val FullInventory(node, machine) = repo.get(n.main.id, AcceptedInventory).testRunGet
+ (
+ machine === Some(m.copy(status = AcceptedInventory)) and
+ node === n
+ .modify(_.main.status)
+ .setTo(AcceptedInventory)
+ .modify(_.machineId)
+ .setTo(Some((m.id, AcceptedInventory)))
+ )
+ }
+ )
+ }
+
+ "don't accept to have a machine in a different status than the node" in {
+ resetStorage
+ val m = machine("differentMachine", AcceptedInventory)
+ val n = node("differentNode", PendingInventory, (m.id, AcceptedInventory))
+ (
+ repo.save(full(n, m)).isOK
+ and {
+ val FullInventory(node, machine) = repo.get(n.main.id, PendingInventory).testRunGet
+ (
+ (node === n.modify(_.machineId).setTo(Some((m.id, PendingInventory)))) and
+ (machine === Some(m.modify(_.status).setTo(PendingInventory)))
+ )
+ }
+ )
+ }
+
+ "use the given machine in full inventory whatever the ID given in node" in {
+ resetStorage
+ val m = machine("invisibleMachine", PendingInventory)
+ val n = node("invisibleNode", PendingInventory, (MachineUuid("something else"), AcceptedInventory))
+ (
+ repo.save(full(n, m)).isOK
+ and {
+ val FullInventory(node, machine) = repo.get(n.main.id, PendingInventory).testRunGet
+
+ (
+ node === n.modify(_.machineId).setTo(Some((m.id, PendingInventory)))
+ and machine === Some(m.modify(_.status).setTo(PendingInventory))
+ )
+ }
+ )
+ }
+
+ "machine, even with the same 'id', are bound to their node" in {
+ resetStorage
+ val m = machine("hardcoreMachine", RemovedInventory)
+ val n0 = node("h-n0", PendingInventory, (m.id, PendingInventory))
+ val n1 = node("h-n1", PendingInventory, (m.id, PendingInventory))
+ val n2 = node("h-n2", AcceptedInventory, (m.id, AcceptedInventory))
+ val n3 = node("h-n3", RemovedInventory, (m.id, RemovedInventory))
+
+ (
+ repo.save(FullInventory(n0, Some(m))).isOK and repo.save(FullInventory(n1, Some(m))).isOK and
+ repo.save(FullInventory(n2, Some(m))).isOK and repo.save(FullInventory(n3, Some(m))).isOK
+ and repo.move(n0.main.id, PendingInventory, AcceptedInventory).isOK
+ and {
+ val FullInventory(node0, m0) = repo.get(n0.main.id, AcceptedInventory).testRunGet
+ val FullInventory(node1, m1) = repo.get(n1.main.id, PendingInventory).testRunGet
+ val FullInventory(node2, m2) = repo.get(n2.main.id, AcceptedInventory).testRunGet
+ val node3 = repo.get(n3.main.id, RemovedInventory).either.runNow
+
+ // expected machine value
+ val n0now = n0.modify(_.main.status).setTo(AcceptedInventory)
+ // update node's machine info to what is normalized
+ def updated(n: NodeInventory) = {
+ (
+ n.modify(_.machineId).setTo(Some((m.id, n.main.status))),
+ m.modify(_.status).setTo(n.main.status)
+ )
+ }
+
+ val (n0_, m0_) = updated(n0now)
+ val (n1_, m1_) = updated(n1)
+ val (n2_, m2_) = updated(n2)
+
+ (
+ m0 === Some(m0_) and m1 === Some(m1_) and m2 === Some(m2_) and
+ node0 === n0_
+ and node1 === n1_
+ and node2 === n2_
+ and (node3 isLeft) // no move to delete
+ )
+ }
+ )
+ }
+
+ }
+
+ "Trying to add specific Windows" should {
+
+ "Allow to save and read it back" in {
+ resetStorage
+ val nodeId = NodeId("windows-2012")
+
+ val node = NodeInventory(
+ NodeSummary(
+ nodeId,
+ AcceptedInventory,
+ "administrator",
+ "localhost",
+ Windows(
+ Windows2012,
+ "foo",
+ new Version("1.0"),
+ None,
+ new Version("1.0")
+ ),
+ NodeId("root"),
+ UndefinedKey
+ ),
+ inventoryDate = Some(DateTime.parse("2023-01-11T10:20:30.000Z")),
+ receiveDate = Some(DateTime.parse("2023-02-22T15:25:35.000Z")),
+ agents = Seq(NodeFact.defaultRudderAgent("administrator").toAgentInfo), // always present now
+ machineId = None
+ )
+
+ repo.save(FullInventory(node, None)).isOK and {
+ val FullInventory(n, m) = repo.get(nodeId, AcceptedInventory).testRunGet
+ // here since we don't have a machine, one is generated with the uuid pattern "machine-for-${nodeId}"
+ n === node.modify(_.machineId).setTo(Some((MachineUuid("machine-for-windows-2012"), AcceptedInventory)))
+ }
+ }
+
+ }
+
+ "Software updates" should {
+
+ "are correctly serialized" in {
+ val d0 = "2022-01-01T00:00:00Z"
+ val dt0 = JsonSerializers.parseSoftwareUpdateDateTime(d0).toOption
+ val id0 = "RHSA-2020-4566"
+ val id1 = "CVE-2021-4034"
+
+ val updates = List(
+ SoftwareUpdate(
+ "app1",
+ Some("2.15.6~RC1"),
+ Some("x86_64"),
+ Some("yum"),
+ SoftwareUpdateKind.Defect,
+ None,
+ Some("Some explanation"),
+ Some(SoftwareUpdateSeverity.Critical),
+ dt0,
+ Some(List(id0, id1))
+ ),
+ SoftwareUpdate(
+ "app2",
+ Some("1-23-RELEASE-1"),
+ Some("x86_64"),
+ Some("apt"),
+ SoftwareUpdateKind.None,
+ Some("default-repo"),
+ None,
+ None,
+ None,
+ None
+ ), // we can have several time the same app
+
+ SoftwareUpdate(
+ "app2",
+ Some("1-24-RELEASE-64"),
+ Some("x86_64"),
+ Some("apt"),
+ SoftwareUpdateKind.Security,
+ Some("security-backports"),
+ None,
+ Some(SoftwareUpdateSeverity.Other("backport")),
+ None,
+ Some(List(id1))
+ )
+ )
+
+ val jsonString = List(
+ s"""{"name":"app1","version":"2.15.6~RC1","arch":"x86_64","from":"yum","kind":"defect","description":"Some explanation","severity":"critical","date":"${d0}","ids":["${id0}","${id1}"]}""",
+ s"""{"name":"app2","version":"1-23-RELEASE-1","arch":"x86_64","from":"apt","kind":"none","source":"default-repo"}""",
+ s"""{"name":"app2","version":"1-24-RELEASE-64","arch":"x86_64","from":"apt","kind":"security","source":"security-backports","severity":"backport","ids":["${id1}"]}"""
+ ).mkString("[", ",", "]")
+
+ import zio.json._
+ import JsonSerializers.implicits._
+
+ updates.toJson === jsonString
+ }
+ }
+
+ step {
+ success
+ }
+
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestSaveInventory.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestSaveInventory.scala
new file mode 100644
index 00000000000..18f8a54d066
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestSaveInventory.scala
@@ -0,0 +1,433 @@
+/*
+ *************************************************************************************
+ * Copyright 2023 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.facts.nodes
+
+import better.files._
+import com.normation.errors._
+import com.normation.inventory.domain._
+import com.normation.inventory.ldap.core.InventoryDit
+import com.normation.inventory.ldap.provisioning._
+import com.normation.inventory.provisioning.fusion.FusionInventoryParser
+import com.normation.inventory.provisioning.fusion.PreInventoryParserCheckConsistency
+import com.normation.inventory.services.provisioning.DefaultInventoryParser
+import com.normation.inventory.services.provisioning.InventoryDigestServiceV1
+import com.normation.inventory.services.provisioning.InventoryParser
+import com.normation.rudder.batch.GitGC
+import com.normation.rudder.domain.nodes.NodeState
+import com.normation.rudder.domain.policies.PolicyMode
+import com.normation.rudder.domain.properties.NodeProperty
+import com.normation.rudder.git.GitRepositoryProviderImpl
+import com.normation.rudder.inventory.DefaultProcessInventoryService
+import com.normation.rudder.inventory.InventoryFailedHook
+import com.normation.rudder.inventory.InventoryMover
+import com.normation.rudder.inventory.InventoryPair
+import com.normation.rudder.inventory.InventoryProcessor
+import com.normation.rudder.inventory.InventoryProcessStatus.Saved
+import com.normation.utils.DateFormaterService
+import com.normation.utils.StringUuidGeneratorImpl
+import com.normation.zio._
+import com.normation.zio.ZioRuntime
+import com.softwaremill.quicklens._
+import com.typesafe.config.ConfigValueFactory
+import cron4s.Cron
+import java.security.Security
+import org.apache.commons.io.FileUtils
+import org.bouncycastle.jce.provider.BouncyCastleProvider
+import org.joda.time.DateTime
+import org.junit.runner._
+import org.specs2.mutable._
+import org.specs2.runner._
+import org.specs2.specification.BeforeAfterAll
+import scala.annotation.nowarn
+import zio._
+import zio.concurrent.ReentrantLock
+import zio.syntax._
+
+/**
+ *
+ * Test the processing of new inventory:
+ * - check that new, never seen nodes end into pending
+ * - check that new, already pending nodes update pending
+ * - check that accepted nodes are updated
+ * - check that signature things work.
+ *
+ * That test does not check for the file observer, only save logic.
+ */
+@RunWith(classOf[JUnitRunner])
+class TestSaveInventoryGit extends TestSaveInventory {
+
+ val GIT_PENDING = basePath + "/fact-repo/nodes/pending"
+
+ def pendingNodeGitFile(id: String) = File(GIT_PENDING + "/" + id + ".json")
+
+ val GIT_ACCEPTED = basePath + "/fact-repo/nodes/accepted"
+
+ def acceptedNodeGitFile(id: String) = File(GIT_ACCEPTED + "/" + id + ".json")
+
+ override def checkPendingNodeExists(id: String): Boolean = pendingNodeGitFile(id).exists
+ override def getPendingNodeAsString(id: String): String = pendingNodeGitFile(id).contentAsString
+ override def checkAcceptedNodeExists(id: String): Boolean = acceptedNodeGitFile(id).exists
+ override def getAcceptedNodeAsString(id: String): String = acceptedNodeGitFile(id).contentAsString
+
+ override lazy val factStorage = {
+ val cronSchedule = Cron.parse("0 42 3 * * ?").toOption
+ val gitFactRepoProvider = GitRepositoryProviderImpl
+ .make(basePath + "/fact-repo")
+ .runOrDie(err => new RuntimeException(s"Error when initializing git configuration repository: " + err.fullMsg))
+ val gitFactRepoGC = new GitGC(gitFactRepoProvider, cronSchedule)
+ gitFactRepoGC.start()
+ // we need to use the default group available, not rudder, else CI complains
+ val storage = new GitNodeFactStorageImpl(gitFactRepoProvider, None, true)
+ storage.checkInit().runOrDie(err => new RuntimeException(s"Error when checking fact repository init: " + err.fullMsg))
+
+ storage
+ }
+}
+
+@RunWith(classOf[JUnitRunner])
+class TestSaveInventoryLdap extends TestSaveInventory {
+
+ def nodeExists(id: String, dit: InventoryDit): Boolean = {
+ MockLdapFactStorage.testServer.entryExists(dit.NODES.NODE.dn(id).toString)
+ }
+
+ def nodeAsString(id: String, dit: InventoryDit): String = {
+ val sb = new java.lang.StringBuilder()
+ // we want to check also for the node entry, not only the inventory part
+ MockLdapFactStorage.testServer.getEntry(dit.NODES.NODE.dn(id).toString).toString(sb)
+ // we want to check also for the node entry, not only the inventory part
+ MockLdapFactStorage.testServer.getEntry(MockLdapFactStorage.nodeDit.NODES.NODE.dn(id).toString).toString(sb)
+ sb.toString()
+ }
+
+ override def checkPendingNodeExists(id: String): Boolean = nodeExists(id, MockLdapFactStorage.pendingDIT)
+ override def getPendingNodeAsString(id: String): String = nodeAsString(id, MockLdapFactStorage.pendingDIT)
+ override def checkAcceptedNodeExists(id: String): Boolean = nodeExists(id, MockLdapFactStorage.acceptedDIT)
+ override def getAcceptedNodeAsString(id: String): String = nodeAsString(id, MockLdapFactStorage.acceptedDIT)
+
+ override lazy val factStorage = MockLdapFactStorage.nodeFactStorage
+}
+
+@nowarn("msg=a type was inferred to be `\\w+`; this may indicate a programming error.")
+trait TestSaveInventory extends Specification with BeforeAfterAll {
+
+ // methods that need to be implemented by children
+ def checkPendingNodeExists(id: String): Boolean
+ def getPendingNodeAsString(id: String): String
+ def checkAcceptedNodeExists(id: String): Boolean
+ def getAcceptedNodeAsString(id: String): String
+
+ // load bouncyCastle
+ Security.addProvider(new BouncyCastleProvider())
+
+ implicit class RunThing[E, T](thing: ZIO[Any, E, T]) {
+ def testRun = ZioRuntime.unsafeRun(thing.either)
+ }
+ implicit class RunOptThing[A](thing: IOResult[Option[A]]) {
+ def testRunGet: A = ZioRuntime.unsafeRun(thing.either) match {
+ case Right(Some(a)) => a
+ case Right(None) => throw new RuntimeException(s"Error in test: found None, expected Some")
+ case Left(err) => throw new RuntimeException(s"Error in test: ${err}")
+ }
+ }
+
+ implicit class TestIsOK[E, T](thing: ZIO[Any, E, T]) {
+ def isOK = thing.testRun must beRight
+ }
+
+ implicit class ForceGetE[E, A](opt: Either[E, A]) {
+ def forceGet: A = opt match {
+ case Right(x) => x
+ case Left(err) => throw new Exception(s"error in Test: ${err}")
+ }
+ }
+
+ implicit def stringToNodeId(id: String): NodeId = NodeId(id)
+
+ val basePath = s"/tmp/test-rudder-inventory/${DateFormaterService.gitTagFormat.print(DateTime.now())}"
+
+ val INVENTORY_ROOT_DIR = basePath + "/inventories"
+ val INVENTORY_DIR_INCOMING = INVENTORY_ROOT_DIR + "/incoming"
+
+ def incomingInventoryFile(name: String) = File(INVENTORY_DIR_INCOMING + "/" + name)
+
+ val INVENTORY_DIR_FAILED = INVENTORY_ROOT_DIR + "/failed"
+ val INVENTORY_DIR_RECEIVED = INVENTORY_ROOT_DIR + "/received"
+
+ def receivedInventoryFile(name: String) = File(INVENTORY_DIR_RECEIVED + "/" + name)
+
+ val INVENTORY_DIR_UPDATE = INVENTORY_ROOT_DIR + "/accepted-nodes-updates"
+
+ override def beforeAll(): Unit = {
+ List(basePath, INVENTORY_DIR_INCOMING, INVENTORY_DIR_FAILED, INVENTORY_DIR_RECEIVED, INVENTORY_DIR_UPDATE).foreach(f =>
+ File(f).createDirectoryIfNotExists(true)
+ )
+
+ }
+
+ override def afterAll(): Unit = {
+ if (java.lang.System.getProperty("tests.clean.tmp") != "false") {
+ FileUtils.deleteDirectory(File(basePath).toJava)
+ }
+ }
+
+ def factStorage: NodeFactStorage
+
+ // TODO WARNING POC: this can't work on a machine with lots of node
+ val callbackLog = Ref.make(Chunk.empty[NodeFactChangeEvent]).runNow
+ def resetLog = callbackLog.set(Chunk.empty).runNow
+ def getLogName = callbackLog.get.map(_.map(_.name)).runNow
+
+ object noopNodeBySoftwareName extends GetNodesbySofwareName {
+ override def apply(softName: String): IOResult[List[(NodeId, Software)]] = {
+ Nil.succeed
+ }
+ }
+
+ val factRepo = {
+ for {
+ pending <- Ref.make(Map[NodeId, CoreNodeFact]())
+ accepted <- Ref.make(Map[NodeId, CoreNodeFact]())
+ callbacks <- Ref.make(Chunk.empty[NodeFactChangeEventCallback])
+ lock <- ReentrantLock.make()
+ r = new CoreNodeFactRepository(factStorage, noopNodeBySoftwareName, pending, accepted, callbacks, lock)
+ _ <- r.registerChangeCallbackAction(CoreNodeFactChangeEventCallback("trail", e => callbackLog.update(_.appended(e.event))))
+// _ <- r.registerChangeCallbackAction(new NodeFactChangeEventCallback("log", e => effectUioUnit(println(s"**** ${e.name}"))))
+ } yield {
+ r
+ }
+ }.runNow
+
+ lazy val inventorySaver = new NodeFactInventorySaver(
+ factRepo,
+ (
+ CheckOsType
+ :: new LastInventoryDate()
+ :: AddIpValues
+ :: Nil
+ ),
+ (
+// we don't want post commit hook in tests
+// new PostCommitInventoryHooks[Unit](HOOKS_D, HOOKS_IGNORE_SUFFIXES)
+ Nil
+ )
+ )
+ lazy val pipelinedInventoryParser: InventoryParser = {
+ val fusionReportParser = {
+ new FusionInventoryParser(
+ new StringUuidGeneratorImpl(),
+ rootParsingExtensions = Nil,
+ contentParsingExtensions = Nil,
+ ignoreProcesses = false
+ )
+ }
+
+ new DefaultInventoryParser(
+ fusionReportParser,
+ Seq(
+ new PreInventoryParserCheckConsistency
+ )
+ )
+ }
+
+ lazy val inventoryProcessorInternal = {
+ new InventoryProcessor(
+ pipelinedInventoryParser,
+ inventorySaver,
+ 4,
+ new InventoryDigestServiceV1(id => factRepo.get(id).map(_.map(cnf => NodeFact.fromMinimal(cnf).toFullInventory))),
+ () => ZIO.unit
+ )
+ }
+
+ lazy val inventoryProcessor = {
+ val mover = new InventoryMover(
+ INVENTORY_DIR_RECEIVED,
+ INVENTORY_DIR_FAILED,
+ new InventoryFailedHook("/tmp", Nil)
+ )
+ new DefaultProcessInventoryService(inventoryProcessorInternal, mover)
+ }
+
+// org.slf4j.LoggerFactory
+// .getLogger("inventory-processing")
+// .asInstanceOf[ch.qos.logback.classic.Logger]
+// .setLevel(ch.qos.logback.classic.Level.TRACE)
+
+ sequential
+
+ val nodeId = "86d9ec77-9db5-4ba3-bdca-f0baf3a5b477"
+ val nodeName = s"node2-${nodeId}.ocs"
+ val nodeResource = s"inventories/7.2/${nodeName}"
+ val newfqdn = "node42.fqdn"
+ val fqdn = "node2.rudder.local"
+
+ implicit val cc: ChangeContext = ChangeContext.newForRudder()
+
+ "Saving a new, unknown inventory" should {
+
+ "correctly save the node in pending" in {
+ resetLog
+ val n2 = incomingInventoryFile(nodeName)
+ n2.write(Resource.getAsString(nodeResource))
+ val n2sign = incomingInventoryFile(s"${nodeName}.sign")
+ n2sign.write(Resource.getAsString(s"${nodeResource}.sign"))
+
+ (inventoryProcessor.saveInventoryBlocking(InventoryPair(n2, n2sign)).runNow must beEqualTo(
+ Saved(nodeName, nodeId)
+ )) and
+ (receivedInventoryFile(nodeName).exists must beTrue) and
+ (checkPendingNodeExists(nodeId) must beTrue) and
+ (factRepo.get(nodeId)(SelectNodeStatus.Pending).runNow must beSome()) and
+ (getLogName === Chunk("newPending"))
+ }
+
+ "change in node by repos are reflected in cold storage" in {
+ implicit val attrs = SelectFacts.none
+ resetLog
+ val e = (for {
+ n <- factRepo.get(nodeId)(SelectNodeStatus.Pending).notOptional("node2 should be there for the test")
+ e <- factRepo.save(NodeFact.fromMinimal(n).modify(_.fqdn).setTo(newfqdn))
+ } yield e).runNow
+
+ (getPendingNodeAsString(nodeId).contains(newfqdn) must beTrue) and
+ (e.event must beAnInstanceOf[NodeFactChangeEvent.UpdatedPending]) and
+ (getLogName === Chunk("updatedPending"))
+
+ }
+
+ "update the node that was modified in repo" in {
+ resetLog
+ val n2 = receivedInventoryFile(nodeName).moveTo(incomingInventoryFile(nodeName))
+ val n2sign = receivedInventoryFile(s"${nodeName}.sign").moveTo(incomingInventoryFile(s"${nodeName}.sign"))
+
+ (inventoryProcessor.saveInventoryBlocking(InventoryPair(n2, n2sign)).runNow must beEqualTo(
+ Saved(nodeName, nodeId)
+ )) and
+ (factRepo.get(nodeId)(SelectNodeStatus.Pending).testRunGet.fqdn must beEqualTo(fqdn)) and
+ (getPendingNodeAsString(nodeId).contains(fqdn) must beTrue) and
+ (getLogName === Chunk("updatedPending"))
+ }
+
+ "rudder settings and properties can be modified on pending nodes" in {
+ val prop = NodeProperty("test-prop-name", ConfigValueFactory.fromAnyRef("test-prop-value"), None, None)
+ implicit val attrs = SelectFacts.none
+ val n = (for {
+ cnf <- factRepo.get(nodeId)(SelectNodeStatus.Pending).notOptional(s"for test - the node was added earlier")
+ up = cnf
+ .modify(_.rudderSettings.state)
+ .setTo(NodeState.Initializing)
+ .modify(_.rudderSettings.policyMode)
+ .setTo(Some(PolicyMode.Audit))
+ .modify(_.properties)
+ .using(_.appended(prop))
+ _ <- factRepo.save(NodeFact.fromMinimal(up))
+ check <- factRepo.get(nodeId)(SelectNodeStatus.Pending).notOptional(s"for test - update node must be here")
+ } yield check).runNow
+
+ val entries = getPendingNodeAsString(nodeId)
+
+ (
+ n.properties must contain(prop)
+ ) and (
+ n.rudderSettings.state === NodeState.Initializing
+ ) and (
+ n.rudderSettings.policyMode === Some(PolicyMode.Audit)
+ ) and (
+ entries must contain("test-prop-name")
+ ) and (
+ entries must contain("test-prop-val")
+ )
+ }
+ }
+
+ "Accepting a new, unknown inventory" should {
+
+ "correctly update status and move file around" in {
+ resetLog
+ val e = factRepo.changeStatus(nodeId, AcceptedInventory).runNow
+ (e.event must beAnInstanceOf[NodeFactChangeEvent.Accepted]) and
+ (checkAcceptedNodeExists(nodeId) must beTrue) and
+ (factRepo.get(nodeId)(SelectNodeStatus.Accepted).testRunGet.rudderSettings.status must beEqualTo(AcceptedInventory)) and
+ (getLogName === Chunk("accepted"))
+ }
+ "change in node by repos are reflected in file" in {
+ resetLog
+ val e = (
+ for {
+ n <- factRepo.get(nodeId)(SelectNodeStatus.Accepted).notOptional("node2 should be there for the test")
+ e <- factRepo.save(NodeFact.fromMinimal(n).modify(_.fqdn).setTo(newfqdn))
+ } yield e
+ ).runNow
+
+ (e.event must beAnInstanceOf[NodeFactChangeEvent.Updated]) and
+ (getAcceptedNodeAsString(nodeId).contains(newfqdn) must beTrue) and
+ (getLogName === Chunk("updatedAccepted"))
+ }
+
+ "update the node that was modified in repo" in {
+ resetLog
+ val n2 = receivedInventoryFile(nodeName).moveTo(incomingInventoryFile(nodeName))
+ val n2sign = receivedInventoryFile(s"${nodeName}.sign").moveTo(incomingInventoryFile(s"${nodeName}.sign"))
+
+ (
+ inventoryProcessor.saveInventoryBlocking(InventoryPair(n2, n2sign)).runNow must beEqualTo(
+ Saved(nodeName, nodeId)
+ )
+ ) and
+ (factRepo.get(nodeId)(SelectNodeStatus.Accepted).testRunGet.fqdn must beEqualTo(fqdn)) and
+ (getAcceptedNodeAsString(nodeId).contains(fqdn) must beTrue) and
+ (getLogName === Chunk("updatedAccepted"))
+ }
+ }
+
+ "Changing status to deleted" should {
+
+ "correctly delete node and value in repos" in {
+ resetLog
+ val e = factRepo.changeStatus(nodeId, RemovedInventory).runNow
+
+ (e.event must beAnInstanceOf[NodeFactChangeEvent.Deleted]) and
+ (checkPendingNodeExists(nodeId) must beFalse) and
+ (checkAcceptedNodeExists(nodeId) must beFalse) and
+ (factRepo.get(nodeId)(SelectNodeStatus.Any).runNow must beNone) and
+ (getLogName === Chunk("deleted"))
+ }
+ }
+
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestSelectFacts.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestSelectFacts.scala
new file mode 100644
index 00000000000..79281a40f88
--- /dev/null
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/facts/nodes/TestSelectFacts.scala
@@ -0,0 +1,199 @@
+/*
+ *************************************************************************************
+ * Copyright 2023 Normation SAS
+ *************************************************************************************
+ *
+ * This file is part of Rudder.
+ *
+ * Rudder is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * In accordance with the terms of section 7 (7. Additional Terms.) of
+ * the GNU General Public License version 3, the copyright holders add
+ * the following Additional permissions:
+ * Notwithstanding to the terms of section 5 (5. Conveying Modified Source
+ * Versions) and 6 (6. Conveying Non-Source Forms.) of the GNU General
+ * Public License version 3, when you create a Related Module, this
+ * Related Module is not considered as a part of the work and may be
+ * distributed under the license agreement of your choice.
+ * A "Related Module" means a set of sources files including their
+ * documentation that, without modification of the Source Code, enables
+ * supplementary functions or services in addition to those offered by
+ * the Software.
+ *
+ * Rudder is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rudder. If not, see .
+
+ *
+ *************************************************************************************
+ */
+
+package com.normation.rudder.facts.nodes
+
+import com.normation.inventory.domain.AcceptedInventory
+import com.normation.inventory.domain.Bios
+import com.normation.inventory.domain.FullInventory
+import com.normation.inventory.domain.MachineInventory
+import com.normation.inventory.domain.MachineUuid
+import com.normation.inventory.domain.Manufacturer
+import com.normation.inventory.domain.VirtualMachineType
+import com.normation.inventory.domain.VmType
+import com.normation.rudder.MockNodes
+import com.softwaremill.quicklens._
+import org.junit.runner._
+import org.specs2.mutable._
+import org.specs2.runner._
+import zio.Chunk
+
+/*
+ * Simple test on merge / mask / etc for SelcetFacts
+ */
+//@silent("a type was inferred to be `\\w+`; this may indicate a programming error.")
+@RunWith(classOf[JUnitRunner])
+class TestSelectFacts extends Specification {
+ import MockNodes._
+
+ val machine1 = MachineInventory(
+ MachineUuid("machine1"),
+ AcceptedInventory,
+ VirtualMachineType(VmType.VirtualBox),
+ None,
+ None,
+ None,
+ None,
+ Some(Manufacturer("the manufactorurer")),
+ None,
+ List(Bios("bios"))
+ )
+
+ val nodeFact1 = NodeFact.fromCompat(node1, Right(FullInventory(nodeInventory1, Some(machine1))), softwares)
+
+ "masking 1" >> {
+ (SelectFacts.mask(nodeFact1)(SelectFacts.all) === nodeFact1) and
+ (nodeFact1.software must not beEmpty) and
+ (nodeFact1.environmentVariables must not beEmpty) and
+ (nodeFact1.bios must not beEmpty) and
+ (nodeFact1.softwareUpdate must not beEmpty)
+ }
+
+ "masking 2" >> {
+ SelectFacts.mask(nodeFact1)(SelectFacts.noSoftware) === nodeFact1.modify(_.software).setTo(Chunk())
+ }
+
+ "masking 3" >> {
+ SelectFacts.mask(nodeFact1)(SelectFacts.none) === nodeFact1
+ .modify(_.swap)
+ .setTo(None)
+ .modify(_.accounts)
+ .setTo(Chunk())
+ .modify(_.bios)
+ .setTo(Chunk())
+ .modify(_.controllers)
+ .setTo(Chunk())
+ .modify(_.environmentVariables)
+ .setTo(Chunk())
+ .modify(_.fileSystems)
+ .setTo(Chunk())
+ .modify(_.inputs)
+ .setTo(Chunk())
+ .modify(_.localGroups)
+ .setTo(Chunk())
+ .modify(_.localUsers)
+ .setTo(Chunk())
+ .modify(_.logicalVolumes)
+ .setTo(Chunk())
+ .modify(_.memories)
+ .setTo(Chunk())
+ .modify(_.networks)
+ .setTo(Chunk())
+ .modify(_.physicalVolumes)
+ .setTo(Chunk())
+ .modify(_.ports)
+ .setTo(Chunk())
+ .modify(_.processes)
+ .setTo(Chunk())
+ .modify(_.processors)
+ .setTo(Chunk())
+ .modify(_.slots)
+ .setTo(Chunk())
+ .modify(_.software)
+ .setTo(Chunk())
+ .modify(_.softwareUpdate)
+ .setTo(Chunk())
+ .modify(_.sounds)
+ .setTo(Chunk())
+ .modify(_.storages)
+ .setTo(Chunk())
+ .modify(_.videos)
+ .setTo(Chunk())
+ .modify(_.vms)
+ .setTo(Chunk())
+ }
+
+ "masking 3" >> {
+ SelectFacts.mask(nodeFact1)(
+ SelectFacts.none
+ .modify(_.bios.mode)
+ .setTo(SelectMode.Retrieve)
+ .modify(_.software.mode)
+ .setTo(SelectMode.Retrieve)
+ .modify(_.softwareUpdate.mode)
+ .setTo(SelectMode.Retrieve)
+ .modify(_.environmentVariables.mode)
+ .setTo(SelectMode.Retrieve)
+ ) === nodeFact1
+ .modify(_.swap)
+ .setTo(None)
+ .modify(_.accounts)
+ .setTo(Chunk())
+// .modify(_.bios)
+// .setTo(Chunk())
+ .modify(_.controllers)
+ .setTo(Chunk())
+// .modify(_.environmentVariables)
+// .setTo(Chunk())
+ .modify(_.fileSystems)
+ .setTo(Chunk())
+ .modify(_.inputs)
+ .setTo(Chunk())
+ .modify(_.localGroups)
+ .setTo(Chunk())
+ .modify(_.localUsers)
+ .setTo(Chunk())
+ .modify(_.logicalVolumes)
+ .setTo(Chunk())
+ .modify(_.memories)
+ .setTo(Chunk())
+ .modify(_.networks)
+ .setTo(Chunk())
+ .modify(_.physicalVolumes)
+ .setTo(Chunk())
+ .modify(_.ports)
+ .setTo(Chunk())
+ .modify(_.processes)
+ .setTo(Chunk())
+ .modify(_.processors)
+ .setTo(Chunk())
+ .modify(_.slots)
+ .setTo(Chunk())
+// .modify(_.software)
+// .setTo(Chunk())
+// .modify(_.softwareUpdate)
+// .setTo(Chunk())
+ .modify(_.sounds)
+ .setTo(Chunk())
+ .modify(_.storages)
+ .setTo(Chunk())
+ .modify(_.videos)
+ .setTo(Chunk())
+ .modify(_.vms)
+ .setTo(Chunk())
+ }
+}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/inventory/TestCertificate.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/inventory/TestCertificate.scala
index 51c573e8640..6f8bd5b9818 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/inventory/TestCertificate.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/inventory/TestCertificate.scala
@@ -46,7 +46,6 @@ import com.normation.inventory.domain.Inventory
import com.normation.inventory.domain.InventoryStatus
import com.normation.inventory.domain.MachineUuid
import com.normation.inventory.domain.NodeId
-import com.normation.inventory.domain.NodeInventory
import com.normation.inventory.domain.SecurityToken.kind
import com.normation.inventory.provisioning.fusion.FusionInventoryParser
import com.normation.inventory.services.core.FullInventoryRepository
@@ -120,15 +119,11 @@ class TestCertificate extends Specification with Loggable {
)
.map(_ => Nil)
- override def getMachineId(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Option[(MachineUuid, InventoryStatus)]] =
+ override def getMachineId(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Option[(MachineUuid, InventoryStatus)]] =
???
- override def getAllInventories(inventoryStatus: InventoryStatus): IOResult[Map[NodeId, FullInventory]] = ???
- override def getInventories(inventoryStatus: InventoryStatus, nodeIds: Set[NodeId]): IOResult[Map[NodeId, FullInventory]] =
- ???
- override def getAllNodeInventories(inventoryStatus: InventoryStatus): IOResult[Map[NodeId, NodeInventory]] = ???
- override def delete(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
- override def move(id: NodeId, from: InventoryStatus, into: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
- override def moveNode(id: NodeId, from: InventoryStatus, into: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
+ override def delete(id: NodeId, inventoryStatus: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
+ override def move(id: NodeId, from: InventoryStatus, into: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
+ override def moveNode(id: NodeId, from: InventoryStatus, into: InventoryStatus): IOResult[Seq[LDIFChangeRecord]] = ???
}
val processor = new InventoryProcessor(
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/repository/jdbc/ReportingServiceTest.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/repository/jdbc/ReportingServiceTest.scala
index 0ad3ab15079..ef4fffde218 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/repository/jdbc/ReportingServiceTest.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/repository/jdbc/ReportingServiceTest.scala
@@ -115,7 +115,7 @@ class ReportingServiceTest extends DBCommon with BoxSpecMatcher {
def getPendingNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = ???
def getDeletedNodeInfos(): IOResult[Map[NodeId, NodeInfo]] = ???
def getDeletedNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = ???
- def getNumberOfManagedNodes: Int = ???
+ def getNumberOfManagedNodes: IOResult[Int] = ???
val getAll: IOResult[Map[NodeId, NodeInfo]] = {
def build(id: String, mode: Option[PolicyMode]) = {
val node1 = NodeConfigData.node1.node
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/nodes/NodeInfoServiceCachedTest.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/nodes/NodeInfoServiceCachedTest.scala
deleted file mode 100644
index 63bb726ee35..00000000000
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/nodes/NodeInfoServiceCachedTest.scala
+++ /dev/null
@@ -1,512 +0,0 @@
-package com.normation.rudder.services.nodes
-
-import com.normation.errors.IOResult
-import com.normation.eventlog.EventActor
-import com.normation.eventlog.ModificationId
-import com.normation.inventory.domain.AcceptedInventory
-import com.normation.inventory.domain.FullInventory
-import com.normation.inventory.domain.InventoryStatus
-import com.normation.inventory.domain.MachineUuid
-import com.normation.inventory.domain.NodeId
-import com.normation.inventory.domain.PendingInventory
-import com.normation.inventory.domain.RemovedInventory
-import com.normation.inventory.ldap.core.FullInventoryRepositoryImpl
-import com.normation.inventory.ldap.core.InventoryDit
-import com.normation.inventory.ldap.core.InventoryDitService
-import com.normation.inventory.ldap.core.InventoryDitServiceImpl
-import com.normation.inventory.ldap.core.InventoryMapper
-import com.normation.inventory.ldap.core.LDAPConstants.A_CONTAINER_DN
-import com.normation.inventory.ldap.core.LDAPConstants.A_DESCRIPTION
-import com.normation.inventory.ldap.core.LDAPConstants.A_HOSTNAME
-import com.normation.inventory.ldap.core.LDAPConstants.A_NAME
-import com.normation.inventory.ldap.core.LDAPConstants.A_NODE_UUID
-import com.normation.inventory.ldap.core.LDAPConstants.A_POLICY_SERVER_UUID
-import com.normation.inventory.ldap.core.LDAPConstants.OC_MACHINE
-import com.normation.inventory.ldap.core.LDAPConstants.OC_NODE
-import com.normation.ldap.ldif.DefaultLDIFFileLogger
-import com.normation.ldap.listener.InMemoryDsConnectionProvider
-import com.normation.ldap.sdk.BuildFilter.AND
-import com.normation.ldap.sdk.BuildFilter.GTEQ
-import com.normation.ldap.sdk.BuildFilter.IS
-import com.normation.ldap.sdk.GeneralizedTime
-import com.normation.ldap.sdk.LDAPConnectionProvider
-import com.normation.ldap.sdk.LDAPEntry
-import com.normation.ldap.sdk.LDAPIOResult.LDAPIOResult
-import com.normation.ldap.sdk.One
-import com.normation.ldap.sdk.RoLDAPConnection
-import com.normation.ldap.sdk.RwLDAPConnection
-import com.normation.rudder.domain.NodeDit
-import com.normation.rudder.domain.RudderDit
-import com.normation.rudder.domain.RudderLDAPConstants.A_POLICY_MODE
-import com.normation.rudder.domain.RudderLDAPConstants.OC_RUDDER_NODE
-import com.normation.rudder.domain.nodes.MachineInfo
-import com.normation.rudder.domain.nodes.Node
-import com.normation.rudder.domain.nodes.NodeInfo
-import com.normation.rudder.domain.nodes.NodeState
-import com.normation.rudder.domain.nodes.NodeState.Enabled
-import com.normation.rudder.domain.policies.PolicyMode
-import com.normation.rudder.reports.ReportingConfiguration
-import com.normation.rudder.repository.ldap.LDAPEntityMapper
-import com.normation.rudder.services.nodes.NodeInfoService.A_MOD_TIMESTAMP
-import com.normation.rudder.services.policies.NodeConfigData
-import com.normation.rudder.services.servers.AcceptFullInventoryInNodeOu
-import com.normation.rudder.services.servers.AcceptInventory
-import com.normation.rudder.services.servers.UnitAcceptInventory
-import com.normation.rudder.services.servers.UnitRefuseInventory
-import com.normation.zio._
-import com.softwaremill.quicklens._
-import com.unboundid.ldap.sdk.DN
-import com.unboundid.ldap.sdk.Filter
-import com.unboundid.ldap.sdk.RDN
-import net.liftweb.common.Box
-import net.liftweb.common.Full
-import org.joda.time.DateTime
-import org.junit.runner.RunWith
-import org.specs2.mutable.Specification
-import org.specs2.runner.JUnitRunner
-import scala.collection.mutable.{Map => MutMap}
-import scala.collection.mutable.Buffer
-import scala.concurrent.duration.FiniteDuration
-
-/*
- * Test the cache behaviour
- */
-@RunWith(classOf[JUnitRunner])
-class NodeInfoServiceCachedTest extends Specification {
-
- sequential
-
- def DN(rdn: String, parent: DN) = new DN(new RDN(rdn), parent)
- val LDAP_BASEDN = new DN("cn=rudder-configuration")
- val LDAP_INVENTORIES_BASEDN = DN("ou=Inventories", LDAP_BASEDN)
- val LDAP_INVENTORIES_SOFTWARE_BASEDN = LDAP_INVENTORIES_BASEDN
-
- val rudderDit = new RudderDit(DN("ou=Rudder", LDAP_BASEDN))
- val nodeDit = new NodeDit(new DN("cn=rudder-configuration"))
- val inventoryDit = InventoryDit(
- DN("ou=Accepted Inventories", DN("ou=Inventories", LDAP_BASEDN)),
- LDAP_INVENTORIES_SOFTWARE_BASEDN,
- "Accepted inventories"
- )
-
- def createNodeInfo(id: NodeId, machineUuid: Option[MachineUuid]): NodeInfo = {
- NodeInfo(
- Node(id, id.value, id.value, Enabled, false, false, new DateTime(), null, null, None),
- id.value,
- machineUuid.map(x => MachineInfo(x, null, None, None)),
- null,
- List(),
- new DateTime(0),
- null,
- Seq(),
- NodeId("root"),
- "root",
- None,
- None,
- None
- )
- }
-
- // create the ldap node ifo, with an option for the machine entry (which is not mandatory)
- def createLdapNodeInfo(
- node: NodeInfo
- ): LDAPNodeInfo = {
- val nodeEntry = nodeDit.NODES.NODE.nodeModel(node.id)
- nodeEntry.resetValuesTo(A_NAME, node.name)
- nodeEntry.resetValuesTo(A_DESCRIPTION, node.node.description)
-
- for {
- mode <- node.node.policyMode
- } nodeEntry.resetValuesTo(A_POLICY_MODE, mode.name)
-
- val machineEntry = node.machine.map(x => inventoryDit.MACHINES.MACHINE.model(x.id))
-
- val nodeInvEntry = inventoryDit.NODES.NODE.genericModel(node.id)
-
- nodeInvEntry.resetValuesTo(A_HOSTNAME, node.name)
- nodeInvEntry.resetValuesTo(A_POLICY_SERVER_UUID, node.policyServerId.value)
- machineEntry.map(mac => nodeInvEntry.resetValuesTo(A_CONTAINER_DN, mac.dn.toString))
-
- LDAPNodeInfo(nodeEntry, nodeInvEntry, machineEntry)
- }
-
- def ldapNodeInfosToMaps(ldapNodeInfos: Seq[LDAPNodeInfo]): NodeInfoServiceCached.InfoMaps = {
- val seqs = ldapNodeInfos.map(x => (x.nodeEntry, x.nodeInventoryEntry, x.machineEntry))
-
- val nodeEntries = MutMap() ++ (for {
- entry <- seqs
- nodeEntry = entry._1
- } yield {
- (nodeEntry.value_!(A_NODE_UUID), nodeEntry)
- }).toMap
- val nodeInventoriesEntries = MutMap() ++ (for {
- entry <- seqs
- nodeInventoryEntry = entry._2
- } yield {
- (nodeInventoryEntry.value_!(A_NODE_UUID), nodeInventoryEntry)
- }).toMap
- val machineEntries = MutMap() ++ (for {
- entry <- seqs
- machineEntry <- entry._3
- machineDn = machineEntry.dn.toString
- } yield {
- (machineDn, machineEntry)
- }).toMap
-
- NodeInfoServiceCached.InfoMaps(nodeEntries, nodeInventoriesEntries, machineEntries, Buffer())
- }
-
- " with a standard cache " should {
- val nodes = Map("1" -> Some("M1"), "2" -> Some("M2"), "3" -> Some("M3"), "4" -> None, "5" -> None)
-
- val nodeInfos = nodes.map {
- case (id, machineId) =>
- createNodeInfo(NodeId(id), machineId.map(MachineUuid(_)))
- }
-
- val ldapNodesInfos = nodeInfos.map {
- case nodeinfo =>
- (nodeinfo.id, (createLdapNodeInfo(nodeinfo), nodeinfo))
- }.toMap
- val cache = LocalNodeInfoCache(ldapNodesInfos, new DateTime(), Seq(), ldapNodesInfos.size)
-
- " be idempotent" in {
- val infoMaps = ldapNodeInfosToMaps(ldapNodesInfos.values.map(_._1).toSeq)
-
- val ldap = NodeInfoServiceCached.constructNodesFromPartialUpdate(cache, infoMaps)
-
- ldapNodesInfos.values.map(_._1).toSeq.sortBy(e => e.nodeEntry.dn.toString) must containTheSameElementsAs(ldap.updated.toSeq)
- }
-
- " find only the new entry if we make a new entry " in {
- val newNode = Map("12" -> Some("M12"))
- val newNodeInfos = newNode.map {
- case (id, machineId) =>
- createNodeInfo(NodeId(id), machineId.map(MachineUuid(_)))
- }
-
- val newLdapNodesInfos = newNodeInfos.map {
- case nodeinfo =>
- (nodeinfo.id, (createLdapNodeInfo(nodeinfo), nodeinfo))
- }.toMap
-
- val infoMaps = ldapNodeInfosToMaps(newLdapNodesInfos.values.map(_._1).toSeq)
-
- val ldap = NodeInfoServiceCached.constructNodesFromPartialUpdate(cache, infoMaps)
-
- newLdapNodesInfos.values.map(_._1).toSeq must containTheSameElementsAs(ldap.updated.toSeq)
- }
-
- "update an existing entry if we update a nodeInventory (policy server) " in {
-
- val nodes = Map("1" -> Some("M1"))
- val nodeInfo = nodes.map {
- case (id, machineId) =>
- createNodeInfo(NodeId(id), machineId.map(MachineUuid(_)))
- }.head
- // reference in cache
- val oldLdapNodeInfo = createLdapNodeInfo(nodeInfo)
- // change the policy server id
- val newNodeInfo = nodeInfo.copy(policyServerId = NodeId("test"))
- val ldapInventoryEntry = createLdapNodeInfo(newNodeInfo).nodeInventoryEntry
-
- val infoMaps = NodeInfoServiceCached.InfoMaps(
- MutMap(),
- MutMap(ldapInventoryEntry.value_!(A_NODE_UUID) -> ldapInventoryEntry),
- MutMap(),
- Buffer()
- )
- val ldap = NodeInfoServiceCached.constructNodesFromPartialUpdate(cache, infoMaps).updated.toSeq
-
- ldap.size == 1 and
- ldap.head.nodeInventoryEntry === ldapInventoryEntry and
- ldap.head.nodeInventoryEntry != oldLdapNodeInfo.nodeInventoryEntry and
- ldap.head.nodeEntry === oldLdapNodeInfo.nodeEntry and
- ldap.head.machineEntry === oldLdapNodeInfo.machineEntry
-
- }
-
- "update an existing entry if we update a node (policy mode) " in {
- val nodes = Map("1" -> Some("M1"))
- val nodeInfo = nodes.map {
- case (id, machineId) =>
- createNodeInfo(NodeId(id), machineId.map(MachineUuid(_)))
- }.head
- // reference in cache
- val oldLdapNodeInfo = createLdapNodeInfo(nodeInfo)
-
- // change the policy mode
- val newNode = nodeInfo.node.copy(policyMode = Some(PolicyMode.Audit))
-
- val newNodeInfo = nodeInfo.copy(node = newNode)
- val ldapNodeEntry = createLdapNodeInfo(newNodeInfo).nodeEntry
-
- val infoMaps =
- NodeInfoServiceCached.InfoMaps(MutMap(ldapNodeEntry.value_!(A_NODE_UUID) -> ldapNodeEntry), MutMap(), MutMap(), Buffer())
-
- val ldap = NodeInfoServiceCached.constructNodesFromPartialUpdate(cache, infoMaps).updated.toSeq
-
- ldap.size == 1 and
- ldap.head.nodeEntry === ldapNodeEntry and
- ldap.head.nodeEntry != oldLdapNodeInfo.nodeEntry and
- ldap.head.nodeInventoryEntry === oldLdapNodeInfo.nodeInventoryEntry and
- ldap.head.machineEntry === oldLdapNodeInfo.machineEntry
-
- }
-
- }
-
- "with a real ldap server" should {
-
- val ldifLogger = new DefaultLDIFFileLogger("TestQueryProcessor", "/tmp/normation/rudder/ldif")
-
- // init of in memory LDAP directory
- val schemaLDIFs = (
- "00-core" ::
- "01-pwpolicy" ::
- "04-rfc2307bis" ::
- "05-rfc4876" ::
- "099-0-inventory" ::
- "099-1-rudder" ::
- Nil
- ) map { name =>
- // toURI is needed for https://issues.rudder.io/issues/19186
- this.getClass.getClassLoader.getResource("ldap-data/schema/" + name + ".ldif").toURI.getPath
- }
- val bootstrapLDIFs = ("ldap/bootstrap.ldif" :: "ldap-data/inventory-sample-data.ldif" :: Nil) map { name =>
- // toURI is needed for https://issues.rudder.io/issues/19186
- this.getClass.getClassLoader.getResource(name).toURI.getPath
- }
- val ldap = InMemoryDsConnectionProvider[RwLDAPConnection](
- baseDNs = "cn=rudder-configuration" :: Nil,
- schemaLDIFPaths = schemaLDIFs,
- bootstrapLDIFPaths = bootstrapLDIFs,
- ldifLogger
- )
- // close your eyes for next line
- val ldapRo = ldap.asInstanceOf[LDAPConnectionProvider[RoLDAPConnection]]
-
- val DIT = new InventoryDit(
- new DN("ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration"),
- new DN("ou=Inventories,cn=rudder-configuration"),
- "test"
- )
-
- val removedDIT = new InventoryDit(
- new DN("ou=Removed Inventories,ou=Inventories,cn=rudder-configuration"),
- new DN("ou=Inventories,cn=rudder-configuration"),
- "test"
- )
- val pendingDIT = new InventoryDit(
- new DN("ou=Pending Inventories,ou=Inventories,cn=rudder-configuration"),
- new DN("ou=Inventories,cn=rudder-configuration"),
- "test"
- )
- val ditService = new InventoryDitServiceImpl(pendingDIT, DIT, removedDIT)
- val nodeDit = new NodeDit(new DN("cn=rudder-configuration"))
- val rudderDit = new RudderDit(new DN("ou=Rudder, cn=rudder-configuration"))
- val inventoryMapper = new InventoryMapper(ditService, pendingDIT, DIT, removedDIT)
- val ldapMapper = new LDAPEntityMapper(rudderDit, nodeDit, DIT, null, inventoryMapper)
- val inventoryDitService: InventoryDitService = new InventoryDitServiceImpl(pendingDIT, DIT, removedDIT)
- val ldapFullInventoryRepository = new FullInventoryRepositoryImpl(inventoryDitService, inventoryMapper, ldap)
- val acceptInventory: UnitAcceptInventory with UnitRefuseInventory =
- new AcceptInventory("accept_new_server:inventory", pendingDIT, DIT, ldapFullInventoryRepository)
- val acceptNodeAndMachineInNodeOu: UnitAcceptInventory with UnitRefuseInventory = new AcceptFullInventoryInNodeOu(
- "accept_new_server:ou=node",
- nodeDit,
- ldap,
- ldapMapper,
- PendingInventory,
- () => Full(None),
- () => Full(NodeState.Enabled)
- )
- /*
- * Our cached node info service. For test, we need to override the search for entries, because the in memory
- * LDAP does not support searching only under some branch AND does not have an entryCSN.
- * So for search, we do 3 search and we always search for "lastMost + 1ms".
- *
- * WARNING: that means that we assume there is no change in the directory between each search, else
- * timestamp of last mod won't be consistant.
- */
- val nodeInfoService = new NodeInfoServiceCachedImpl(
- ldapRo,
- nodeDit,
- DIT,
- removedDIT,
- pendingDIT,
- ldapMapper,
- inventoryMapper,
- FiniteDuration(5, "millis")
- ) {
- override def getNodeInfoEntries(
- con: RoLDAPConnection,
- searchAttributes: Seq[String],
- status: InventoryStatus,
- lastModification: Option[DateTime]
- ): LDAPIOResult[Seq[LDAPEntry]] = {
- // for test, force to not look pending
- val dit = status match {
- case AcceptedInventory | PendingInventory => this.inventoryDit
- case RemovedInventory => this.removedDit
- }
-
- def filter(filterNodes: Filter) = lastModification match {
- case None => filterNodes
- case Some(d) => AND(filterNodes, GTEQ(A_MOD_TIMESTAMP, GeneralizedTime(d.plus(1)).toString))
- }
-
- for {
- res1 <- con.search(dit.NODES.dn, One, filter(IS(OC_NODE)), searchAttributes: _*)
- res2 <- con.search(dit.MACHINES.dn, One, filter(IS(OC_MACHINE)), searchAttributes: _*)
- res3 <- con.search(this.nodeDit.NODES.dn, One, filter(IS(OC_RUDDER_NODE)), searchAttributes: _*)
- } yield res1 ++ res2 ++ res3
- }
- }
-
- implicit class ForceGetBox[A](b: Box[A]) {
- def forceGet = b match {
- case Full(a) => a
- case eb => throw new IllegalArgumentException(s"Error during test, box is an erro: ${eb}")
- }
- }
- implicit class ForceGetIO[A](b: IOResult[A]) {
- def forceGet = b.either.runNow match {
- case Right(a) => a
- case Left(err) => throw new IllegalArgumentException(s"Error during test, box is an erro: ${err.fullMsg}")
- }
- }
-
- "a new entry, with only node and then on next cache update inventory is ignored then found" in {
- // we have a new node. In NewNodeManager impl in RudderConfig, we start by acceptNodeAndMachineInNodeOu then acceptInventory
-
- // our new node
- val nodeInv = new FullInventory(
- NodeConfigData.nodeInventory1
- .modify(_.main.status)
- .setTo(PendingInventory)
- .modify(_.main.id.value)
- .setTo("testCacheNode")
- .modify(_.machineId)
- .setTo(Some((MachineUuid("testCacheMachine"), PendingInventory))),
- Some(
- NodeConfigData.machine2Pending
- .modify(_.id.value)
- .setTo("testCacheMachine")
- .modify(_.name)
- .setTo(Some("testCacheMachine"))
- )
- )
- val modid = ModificationId("test")
- val actor = EventActor("test")
- val nodeId = nodeInv.node.main.id
-
- // *************** start ****************
- // add inventory to pending
- ldapFullInventoryRepository.save(nodeInv).runNow
- // wait a bit for cache
- Thread.sleep(50)
- // load cache
- nodeInfoService.getAll().forceGet
-
- // org.slf4j.LoggerFactory.getLogger("nodes.cache").asInstanceOf[ch.qos.logback.classic.Logger].setLevel(ch.qos.logback.classic.Level.TRACE)
-
- // *************** step1 ****************
- // cache does not know about node1 yet
- acceptNodeAndMachineInNodeOu.acceptOne(nodeInv, modid, actor).forceGet
- val step1res = nodeInfoService.getNodeInfo(nodeId).forceGet
-
- // *************** step2 ****************
- // second new node step: cache converge
- acceptInventory.acceptOne(nodeInv, modid, actor).forceGet
- val step2res = nodeInfoService.getNodeInfo(nodeId).forceGet
-
- (step1res === None) and
- (step2res must beSome) and
- (step2res.get.machine must beSome)
-
- }
-
- "a new entry, with only the container first, and then on next cache update inventory is ignored then found" in {
- // our new node
- val nodeInv = new FullInventory(
- NodeConfigData.nodeInventory1
- .modify(_.main.status)
- .setTo(PendingInventory)
- .modify(_.main.id.value)
- .setTo("testCacheNode2")
- .modify(_.machineId)
- .setTo(Some((MachineUuid("testCacheMachine2"), AcceptedInventory))),
- Some(
- NodeConfigData.machine1Accepted
- .modify(_.id.value)
- .setTo("testCacheMachine2")
- .modify(_.name)
- .setTo(Some("testCacheMachine2"))
- )
- )
- val nodeId = nodeInv.node.main.id
-
- /** Create the node here, to "cheat" to simulate acceptation */
- val name = nodeInv.node.name.getOrElse(nodeInv.node.main.id.value)
- val description = nodeInv.node.description.getOrElse("")
-
- val node = Node(
- nodeId,
- name,
- description,
- NodeState.Enabled,
- false,
- false,
- DateTime.now, // won't be used on save - dummy value
-
- ReportingConfiguration(None, None, None), // use global schedule, and default configuration for reporting
-
- Nil, // no user properties for now
-
- None
- )
-
- // *************** start ****************
- // Force init of cache
- nodeInfoService.getAll().forceGet
-
- // add node inventory to pending, but machine to accepted
- ldapFullInventoryRepository.save(nodeInv).runNow
- ldap.server.exportToLDIF("/tmp/ldif-before", false, false)
-
- // wait a bit for cache
- Thread.sleep(500)
- // load cache
- nodeInfoService.getAll().forceGet
-
- // *************** step1 ****************
- // cache does not know about testCacheNode2 yet
- val step1res = nodeInfoService.getNodeInfo(nodeId).forceGet
-
- // move the node to the accepted
- // *************** step2 ****************
- ldapFullInventoryRepository.moveNode(nodeId, PendingInventory, AcceptedInventory).runNow
-
- // create the entry in ou=Nodes
- val nodeEntry = ldapMapper.nodeToEntry(node)
- (for {
- con <- ldap
- saved <- con.save(nodeEntry)
- } yield {
- saved
- }).runNow
-
- ldapFullInventoryRepository.move(MachineUuid("testCacheMachine2"), PendingInventory)
-
- Thread.sleep(500)
- val step2res = nodeInfoService.getNodeInfo(nodeId).forceGet
- // It should find the container via compensation
-
- (step1res === None) and
- (step2res must beSome) and
- (step2res.get.machine must beSome)
-
- }
- }
-
-}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/nodes/TestMergeGroupProperties.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/nodes/TestMergeGroupProperties.scala
index c4f26b83466..fb252238299 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/nodes/TestMergeGroupProperties.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/nodes/TestMergeGroupProperties.scala
@@ -77,7 +77,7 @@ class TestMergeGroupProperties extends Specification {
implicit class ToTarget(g: NodeGroup) {
def toTarget = FullRuleTargetInfo(FullGroupTarget(GroupTarget(g.id), g), g.name, "", true, true)
- def toCriterion = CriterionLine(null, Criterion("some ldap attr", new SubGroupComparator(null)), null, g.id.serialize)
+ def toCriterion = CriterionLine(null, Criterion("some ldap attr", SubGroupComparator(null), null), null, g.id.serialize)
}
implicit class ToNodePropertyHierarchy(groups: List[NodeGroup]) {
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestQueryProcessor.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestNodeFactQueryProcessor.scala
similarity index 87%
rename from webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestQueryProcessor.scala
rename to webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestNodeFactQueryProcessor.scala
index 7f3ef0a039a..6cc083f615b 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestQueryProcessor.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestNodeFactQueryProcessor.scala
@@ -37,24 +37,23 @@
package com.normation.rudder.services.queries
+import com.normation.NamedZioLogger
import com.normation.errors._
import com.normation.inventory.domain.NodeId
-import com.normation.inventory.ldap.core._
-import com.normation.ldap.ldif._
-import com.normation.ldap.listener.InMemoryDsConnectionProvider
-import com.normation.ldap.sdk._
-import com.normation.rudder.domain._
+import com.normation.inventory.domain.Software
+import com.normation.rudder.domain.RudderDit
+import com.normation.rudder.domain.nodes.NodeGroupId
+import com.normation.rudder.domain.nodes.NodeGroupUid
import com.normation.rudder.domain.queries._
-import com.normation.rudder.repository.ldap.LDAPEntityMapper
-import com.normation.rudder.services.nodes.NaiveNodeInfoServiceCachedImpl
+import com.normation.rudder.facts.nodes._
import com.normation.zio._
import com.softwaremill.quicklens._
import com.unboundid.ldap.sdk.DN
-import net.liftweb.common._
+import net.liftweb.common.Failure
import org.junit._
-import org.junit.Assert._
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
+import zio._
import zio.syntax._
/*
@@ -65,80 +64,84 @@ import zio.syntax._
*/
@RunWith(classOf[BlockJUnit4ClassRunner])
-class TestQueryProcessor extends Loggable {
-
- val ldifLogger = new DefaultLDIFFileLogger("TestQueryProcessor", "/tmp/normation/rudder/ldif")
-
- // init of in memory LDAP directory
- val schemaLDIFs = (
- "00-core" ::
- "01-pwpolicy" ::
- "04-rfc2307bis" ::
- "05-rfc4876" ::
- "099-0-inventory" ::
- "099-1-rudder" ::
- Nil
- ) map { name =>
- // toURI is needed for https://issues.rudder.io/issues/19186
- this.getClass.getClassLoader.getResource("ldap-data/schema/" + name + ".ldif").toURI.getPath
- }
- val bootstrapLDIFs = ("ldap/bootstrap.ldif" :: "ldap-data/inventory-sample-data.ldif" :: Nil) map { name =>
- // toURI is needed for https://issues.rudder.io/issues/19186
- this.getClass.getClassLoader.getResource(name).toURI.getPath
- }
- val ldap = InMemoryDsConnectionProvider[RoLDAPConnection](
- baseDNs = "cn=rudder-configuration" :: Nil,
- schemaLDIFPaths = schemaLDIFs,
- bootstrapLDIFPaths = bootstrapLDIFs,
- ldifLogger
- )
- // end inMemory ds
+class TestNodeFactQueryProcessor {
+ implicit def StringToNodeId(s: String): NodeId = NodeId(s)
+ implicit def StringToGroupId(s: String): NodeGroupId = NodeGroupId(NodeGroupUid(s))
+
+ val logger = NamedZioLogger(this.getClass.getPackageName + "." + this.getClass.getSimpleName)
+
+ object subGroupComparatorRepo extends SubGroupComparatorRepository {
+ val groups = Map(
+ (SubGroupChoice("test-group-node1", "Only contains node1"), Chunk[NodeId]("node1")),
+ (SubGroupChoice("test-group-node2", "Only contains node2"), Chunk[NodeId]("node2")),
+ (SubGroupChoice("test-group-node12", "Only contains node1 and node2"), Chunk[NodeId]("node1", "node2")),
+ (SubGroupChoice("test-group-node23", "Only contains node2 and node3"), Chunk[NodeId]("node2", "node3")),
+ (SubGroupChoice("AIXSystems", "AIXSystems"), Chunk[NodeId]())
+ )
+
+ override def getNodeIds(groupId: NodeGroupId): IOResult[Chunk[NodeId]] = {
+ (groups.find(_._1.id == groupId) match {
+ case Some(kv) => kv._2
+ case None => Chunk.empty
+ }).succeed
+ }
- val DIT = new InventoryDit(
- new DN("ou=Accepted Inventories,ou=Inventories,cn=rudder-configuration"),
- new DN("ou=Inventories,cn=rudder-configuration"),
- "test"
- )
+ override def getGroups: IOResult[Chunk[SubGroupChoice]] = Chunk.fromIterable(groups.keys).succeed
+ }
+ val queryData = new NodeQueryCriteriaData(() => subGroupComparatorRepo)
+
+ // load all nodes that in resources: node-facts/*.json
+// java.lang.Runtime.getRuntime.gc()
+// println(s"free memory before: " + java.lang.Runtime.getRuntime.freeMemory())
+// val nodes = File(Resource.getUrl("node-facts").getPath).children.toList.flatMap { f =>
+// if (f.extension != Some(".json")) None
+// else {
+// f.contentAsString(StandardCharsets.UTF_8).fromJson[NodeFact] match {
+// case Left(err) => throw new IllegalArgumentException(s"Unable to read node from file '${f.pathAsString}': ${err}")
+// case Right(n) => Some(n)
+// }
+// }
+// }
- val removedDIT = new InventoryDit(
- new DN("ou=Removed Inventories,ou=Inventories,cn=rudder-configuration"),
- new DN("ou=Inventories,cn=rudder-configuration"),
- "test"
- )
- val pendingDIT = new InventoryDit(
- new DN("ou=Pending Inventories,ou=Inventories,cn=rudder-configuration"),
- new DN("ou=Inventories,cn=rudder-configuration"),
- "test"
- )
- val ditService = new InventoryDitServiceImpl(pendingDIT, DIT, removedDIT)
- val nodeDit = new NodeDit(new DN("cn=rudder-configuration"))
- val rudderDit = new RudderDit(new DN("ou=Rudder, cn=rudder-configuration"))
+ // when one need to debug search, you can just uncomment that to set log-level to trace
+ // format: off
+ //org.slf4j.LoggerFactory.getLogger("com.normation.rudder.services.queries").asInstanceOf[ch.qos.logback.classic.Logger].setLevel(ch.qos.logback.classic.Level.TRACE)
+ // format: on
- val ditQueryData = new DitQueryData(DIT, nodeDit, rudderDit, () => Inconsistency("For test, no subgroup").fail)
+ val nodeRepository = {
- val inventoryMapper = new InventoryMapper(ditService, pendingDIT, DIT, removedDIT)
- val ldapMapper = new LDAPEntityMapper(rudderDit, nodeDit, DIT, null, inventoryMapper)
- val internalLDAPQueryProcessor = new InternalLDAPQueryProcessor(ldap, DIT, nodeDit, ditQueryData, ldapMapper)
+ object NoopNodeBySoftware extends GetNodesbySofwareName {
+ override def apply(softName: String): IOResult[List[(NodeId, Software)]] = Nil.succeed
+ }
+ CoreNodeFactRepository
+ .make(
+ MockLdapFactStorage.nodeFactStorage,
+ NoopNodeBySoftware,
+ Chunk.empty
+ )
+ .runNow
+ }
- val nodeInfoService =
- new NaiveNodeInfoServiceCachedImpl(ldap, nodeDit, DIT, removedDIT, pendingDIT, ldapMapper, inventoryMapper)
+ val internalLDAPQueryProcessor = {
+ import MockLdapFactStorage._
+ val rudderDit = new RudderDit(new DN("ou=Rudder, cn=rudder-configuration"))
+ val ditQueryData = new DitQueryData(acceptedDIT, nodeDit, rudderDit, queryData)
+ new InternalLDAPQueryProcessor(ldapRo, acceptedDIT, nodeDit, ditQueryData, ldapMapper)
+ }
- val queryProcessor = new AcceptedNodesLDAPQueryProcessor(
- nodeDit,
- DIT,
- internalLDAPQueryProcessor,
- nodeInfoService
- )
+ val queryProcessor = new NodeFactQueryProcessor(nodeRepository, subGroupComparatorRepo, internalLDAPQueryProcessor)
val parser = new CmdbQueryParser with DefaultStringQueryParser with JsonQueryLexer {
- override val criterionObjects = Map[String, ObjectCriterion]() ++ ditQueryData.criteriaMap
+ override val criterionObjects = queryData.criteriaMap.toMap
}
case class TestQuery(name: String, query: Query, awaited: Seq[NodeId])
// when one need to debug search, you can just uncomment that to set log-level to trace
- // val l: ch.qos.logback.classic.Logger = org.slf4j.LoggerFactory.getLogger("com.normation.rudder.services.queries").asInstanceOf[ch.qos.logback.classic.Logger]
- // l.setLevel(ch.qos.logback.classic.Level.TRACE)
+ org.slf4j.LoggerFactory
+ .getLogger("query.node-fact")
+ .asInstanceOf[ch.qos.logback.classic.Logger]
+ .setLevel(ch.qos.logback.classic.Level.TRACE)
val s = Seq(
new NodeId("node0"),
@@ -154,22 +157,6 @@ class TestQueryProcessor extends Loggable {
val root = NodeId("root")
val sr = root +: s
- @Test def ensureNodeLoaded(): Unit = {
- // just check that we correctly loaded demo data in serve
- val s = (for {
- con <- ldap
- res <- con.search(new DN("cn=rudder-configuration"), Sub, BuildFilter.ALL)
- } yield {
- res.size
- }).runNow
-
- val expected = 43 + 40 // bootstrap + inventory-sample
- assert(
- expected == s,
- s"Not found the expected number of entries in test LDAP directory [expected: ${expected}, found: ${s}], perhaps the demo entries where not correctly loaded"
- )
- }
-
@Test def basicQueriesOnId(): Unit = {
/* find back all server */
@@ -261,18 +248,7 @@ class TestQueryProcessor extends Loggable {
q2_2.awaited
)
- // group of group, with or/and composition
- val q3 = TestQuery(
- "q3",
- parser("""
- { "select":"node", "where":[
- { "objectType":"group", "attribute":"nodeGroupId", "comparator":"eq", "value":"test-group-node1" }
- ] }
- """).openOrThrowException("For tests"),
- s(1) :: Nil
- )
-
- testQueries(q2_0 :: q2_0_ :: q2_1 :: q2_1_ :: q2_2 :: q2_2_ :: q3 :: Nil, true)
+ testQueries(q2_0 :: q2_0_ :: q2_1 :: q2_1_ :: q2_2 :: q2_2_ :: Nil, true)
}
// group of group, with or/and composition
@@ -779,7 +755,7 @@ class TestQueryProcessor extends Loggable {
query = q2.query.copy(composition = Or),
(s(2) :: s(7) :: // software
s(4) :: s(5) :: s(6) :: s(7) :: // machine
- s(2) :: root :: // free space
+ s(7) :: root :: // free space
s(2) :: // bios
Nil).distinct
)
@@ -928,32 +904,33 @@ class TestQueryProcessor extends Loggable {
def q(name: String, comp: String, day: Int, expects: Seq[NodeId]) = TestQuery(
name,
parser("""
- { "select":"node", "where":[
+ { "select":"nodeAndPolicyServer", "where":[
{ "objectType":"node", "attribute":"inventoryDate", "comparator":"%s" , "value":"%s/05/2013" }
] }
""".format(comp, day)).openOrThrowException("For tests"),
expects
)
- def query(name: String, comp: String, day: Int, valid: Boolean) = q(name, comp, day, if (valid) s(0) :: Nil else Nil)
-
- val q12 = q("q12", "notEq", 15, s.filterNot(_ == s(0)))
- val q13 = q("q13", "notEq", 14, s)
- val q14 = q("q14", "notEq", 16, s)
+ // nodes are going year by year [root=2012-05-15, s0=2013-05-15 <- select date, s1=2014-05-15 etc]
+ def query(name: String, comp: String, day: Int, nodes: Seq[NodeId]) = q(name, comp, day, nodes)
+ // root is not part of 's", no need to filter it out
+ // the number 14, 15 etc is the day in 2013-05-dd !
testQueries(
- query("q1", "eq", 15, valid = true)
- :: query("q2", "eq", 14, valid = false)
- :: query("q3", "eq", 16, valid = false)
- :: query("q4", "gteq", 15, valid = true)
- :: query("q5", "gteq", 16, valid = false)
- :: query("q6", "lteq", 15, valid = true)
- :: query("q7", "lteq", 14, valid = false)
- :: query("q8", "lt", 15, valid = false)
- :: query("q9", "lt", 16, valid = true)
- :: query("q10", "gt", 15, valid = false)
- :: query("q11", "gt", 14, valid = true)
- :: q12 :: q13 :: q14
+ query("q1", "eq", 15, s(0) :: Nil)
+ :: query("q2", "eq", 14, Nil)
+ :: query("q3", "eq", 16, Nil)
+ :: query("q4", "gteq", 15, s)
+ :: query("q5", "gteq", 16, s.filterNot(x => x == s(0)))
+ :: query("q6", "lteq", 15, root :: s(0) :: Nil)
+ :: query("q7", "lteq", 14, root :: Nil)
+ :: query("q8", "lt", 15, root :: Nil)
+ :: query("q9", "lt", 16, root :: s(0) :: Nil)
+ :: query("q10", "gt", 15, s.filterNot(x => x == s(0)))
+ :: query("q11", "gt", 14, s)
+ :: q("q12", "notEq", 15, root +: s.filterNot(_ == s(0)))
+ :: q("q13", "notEq", 14, root +: s)
+ :: q("q14", "notEq", 16, root +: s)
:: Nil,
true
)
@@ -1328,7 +1305,7 @@ class TestQueryProcessor extends Loggable {
val results = failingRegexRequests.map(q => (q, queryProcessor.process(forceParse(q))))
results.foreach { r =>
- assertTrue(s"Regex Query with wrong data for node properties should fail: ${r._1}", r._2.isInstanceOf[Failure])
+ Assert.assertTrue(s"Regex Query with wrong data for node properties should fail: ${r._1}", r._2.isInstanceOf[Failure])
}
}
@@ -1350,7 +1327,7 @@ class TestQueryProcessor extends Loggable {
private def testQueries(queries: Seq[TestQuery], doInternalQueryTest: Boolean): Unit = {
queries foreach { q =>
- logger.debug("Processing: " + q.name)
+ logger.logEffect.debug("Processing: " + q.name)
testQueryResultProcessor(q.name, q.query, q.awaited, doInternalQueryTest)
}
@@ -1359,49 +1336,25 @@ class TestQueryProcessor extends Loggable {
private def testQueryResultProcessor(name: String, query: Query, nodes: Seq[NodeId], doInternalQueryTest: Boolean) = {
val ids = nodes.sortBy(_.value)
val found = queryProcessor.process(query).openOrThrowException("For tests").sortBy(_.value)
- // also test with requiring only the expected node to check consistancy
+ // also test with requiring only the expected node to check consistency
// (that should not change anything)
- assertEquals(
+ Assert.assertEquals(
s"[$name] Duplicate entries in result: $found",
found.size.toLong,
found.distinct.size.toLong
)
- assertEquals(
+ Assert.assertEquals(
s"[$name] Size differs between expected and found entries (process method)\n Found: $found \n Expected: ${ids}",
ids.size.toLong,
found.size.toLong
)
- assertTrue(
+ Assert.assertTrue(
s"[$name] Nodes found are different from expected Nodes (process method)\n Found: ${found}\n Expected: ${ids}",
found.forall(f => ids.exists(f == _))
)
- if (doInternalQueryTest) {
- logger.debug(
- "Testing with expected entries, This test should be ignored when we are looking for Nodes with NodeInfo and inventory (ie when we are looking for property and environement variable"
- )
- val foundWithLimit = {
- (internalLDAPQueryProcessor
- .internalQueryProcessor(
- query,
- limitToNodeIds = Some(ids),
- lambdaAllNodeInfos = (() => nodeInfoService.getAllNodeInfos())
- )
- .runNow)
- .distinct
- .sortBy(_.value)
- }
-
- assertEquals(
- s"[${name}] Size differs between expected and found entries (InternalQueryProcessor, only inventory fields)\n Found: ${foundWithLimit}\n Expected: ${ids}",
- ids.size.toLong,
- foundWithLimit.size.toLong
- )
- }
}
- @After def after(): Unit = {
- ldap.server.shutDown(true)
- }
+ @After def after(): Unit = {}
}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestPendingNodePolicies.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestPendingNodePolicies.scala
index 567f5365240..f16dc8bb9c5 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestPendingNodePolicies.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestPendingNodePolicies.scala
@@ -51,6 +51,7 @@ import com.normation.rudder.domain.queries.Criterion
import com.normation.rudder.domain.queries.CriterionLine
import com.normation.rudder.domain.queries.Equals
import com.normation.rudder.domain.queries.ExactStringComparator
+import com.normation.rudder.domain.queries.NodeCriterionMatcherString
import com.normation.rudder.domain.queries.ObjectCriterion
import com.normation.rudder.domain.queries.Or
import com.normation.rudder.domain.queries.Query
@@ -63,6 +64,7 @@ import org.joda.time.DateTime
import org.junit.runner.RunWith
import org.specs2.mutable.Specification
import org.specs2.runner.JUnitRunner
+import zio.Chunk
import zio.syntax._
/**
@@ -101,7 +103,7 @@ class TestPendingNodePolicies extends Specification {
val groupCriterion = ObjectCriterion(
"group",
Seq(
- Criterion(A_NODE_GROUP_UUID, ExactStringComparator)
+ Criterion(A_NODE_GROUP_UUID, ExactStringComparator, NodeCriterionMatcherString(_ => Chunk("group id")))
)
)
@@ -109,8 +111,11 @@ class TestPendingNodePolicies extends Specification {
def sub(g: NodeGroup) = CriterionLine(groupCriterion, groupCriterion.criteria.head, Equals, g.id.serialize)
// a random query that will be added as dummy content - query checker will returns pre-defined things
val cl = CriterionLine(
- ObjectCriterion(OC_MACHINE, Seq(Criterion(A_MACHINE_UUID, StringComparator))),
- Criterion(A_MACHINE_UUID, StringComparator),
+ ObjectCriterion(
+ OC_MACHINE,
+ Seq(Criterion(A_MACHINE_UUID, StringComparator, NodeCriterionMatcherString(n => Chunk(n.machine.id.value))))
+ ),
+ Criterion(A_MACHINE_UUID, StringComparator, NodeCriterionMatcherString(n => Chunk(n.machine.id.value))),
Equals,
"dummy"
)
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestStringQueryParser.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestStringQueryParser.scala
index bb33f99e0a9..25ad6f6b37d 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestStringQueryParser.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/queries/TestStringQueryParser.scala
@@ -44,6 +44,7 @@ import org.junit._
import org.junit.Assert._
import org.junit.runner.RunWith
import org.junit.runners.BlockJUnit4ClassRunner
+import zio.Chunk
@RunWith(classOf[BlockJUnit4ClassRunner])
class TestStringQueryParser {
@@ -51,12 +52,10 @@ class TestStringQueryParser {
/*
* our data store:
* two criteria
- *
- *
*/
- val c1 = Criterion("name", BareComparator(Exists, Greater))
- val c2 = Criterion("id", BareComparator(Equals))
- val c3 = Criterion("name", BareComparator(Exists, Greater))
+ val c1 = Criterion("name", BareComparator(Exists, Greater), NodeCriterionMatcherString(_ => Chunk("something")))
+ val c2 = Criterion("id", BareComparator(Equals), NodeCriterionMatcherString(_ => Chunk("something")))
+ val c3 = Criterion("name", BareComparator(Exists, Greater), NodeCriterionMatcherString(_ => Chunk("something")))
val oc1 = ObjectCriterion("node", List(c1, c2))
val oc2 = ObjectCriterion("machine", List(c3))
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/reports/CachedFindRuleNodeStatusReportsTest.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/reports/CachedFindRuleNodeStatusReportsTest.scala
index 0a9d94634ff..856cb2c5c74 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/reports/CachedFindRuleNodeStatusReportsTest.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/reports/CachedFindRuleNodeStatusReportsTest.scala
@@ -143,7 +143,7 @@ class CachedFindRuleNodeStatusReportsTest extends Specification {
def getPendingNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = ???
def getDeletedNodeInfos(): IOResult[Map[NodeId, NodeInfo]] = ???
def getDeletedNodeInfo(nodeId: NodeId): IOResult[Option[NodeInfo]] = ???
- def getNumberOfManagedNodes: Int = ???
+ def getNumberOfManagedNodes: IOResult[Int] = ???
val getAll: IOResult[Map[NodeId, NodeInfo]] = {
nodes.map { case (n, _, _) => (n.id, n) }.toMap.succeed
}
diff --git a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/servers/TestRemoveNodeService.scala b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/servers/TestRemoveNodeService.scala
index f04281bd0b6..9f8d365764d 100644
--- a/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/servers/TestRemoveNodeService.scala
+++ b/webapp/sources/rudder/rudder-core/src/test/scala/com/normation/rudder/services/servers/TestRemoveNodeService.scala
@@ -38,7 +38,9 @@ package com.normation.rudder.services.servers
import better.files._
import com.normation.eventlog.EventActor
+import com.normation.eventlog.ModificationId
import com.normation.inventory.domain.NodeId
+import com.normation.rudder.facts.nodes.ChangeContext
import com.normation.zio._
import org.joda.time.DateTime
import org.joda.time.format.ISODateTimeFormat
@@ -91,13 +93,15 @@ class TestRemoveNodeService extends Specification with AfterAll {
)
val cleanUp = new CleanUpNodePolicyFiles(varRudderShare.pathAsString)
+ implicit val testChangeContext: ChangeContext =
+ ChangeContext(ModificationId("test-mod-id"), EventActor("test"), DateTime.now(), None, None)
/*
*
*/
"Policy directory should be cleaned" >> {
startFS.foreach(_.createDirectories())
- cleanUp.run(NodeId("nodeXX"), DeleteMode.Erase, None, Set(), EventActor("test")).runNow
+ cleanUp.run(NodeId("nodeXX"), DeleteMode.Erase, None, Set()).runNow
val files = varRudderShare.collectChildren(_ => true).toList.map(_.pathAsString)
files must containTheSameElementsAs(expected.map(_.pathAsString))
diff --git a/webapp/sources/rudder/rudder-rest/src/main/scala/com/normation/rudder/rest/lift/NodeApi.scala b/webapp/sources/rudder/rudder-rest/src/main/scala/com/normation/rudder/rest/lift/NodeApi.scala
index 42233d88da3..cc1128d0fba 100644
--- a/webapp/sources/rudder/rudder-rest/src/main/scala/com/normation/rudder/rest/lift/NodeApi.scala
+++ b/webapp/sources/rudder/rudder-rest/src/main/scala/com/normation/rudder/rest/lift/NodeApi.scala
@@ -47,8 +47,6 @@ import com.normation.eventlog.ModificationId
import com.normation.inventory.domain._
import com.normation.inventory.domain.NodeId
import com.normation.inventory.ldap.core.InventoryDit
-import com.normation.inventory.ldap.core.LDAPFullInventoryRepository
-import com.normation.inventory.services.core.ReadOnlySoftwareDAO
import com.normation.ldap.sdk.LDAPConnectionProvider
import com.normation.ldap.sdk.RwLDAPConnection
import com.normation.rudder.UserService
@@ -59,7 +57,6 @@ import com.normation.rudder.apidata.RestDataSerializer
import com.normation.rudder.batch.AsyncDeploymentActor
import com.normation.rudder.batch.AutomaticStartDeployment
import com.normation.rudder.domain.NodeDit
-import com.normation.rudder.domain.logger.ApiLoggerPure
import com.normation.rudder.domain.logger.NodeLogger
import com.normation.rudder.domain.logger.NodeLoggerPure
import com.normation.rudder.domain.logger.TimingDebugLoggerPure
@@ -74,6 +71,11 @@ import com.normation.rudder.domain.properties.NodeProperty
import com.normation.rudder.domain.properties.NodePropertyHierarchy
import com.normation.rudder.domain.queries.Query
import com.normation.rudder.domain.reports.ComplianceLevel
+import com.normation.rudder.facts.nodes.ChangeContext
+import com.normation.rudder.facts.nodes.NodeFact
+import com.normation.rudder.facts.nodes.NodeFactFullInventoryRepositoryProxy
+import com.normation.rudder.facts.nodes.NodeFactRepository
+import com.normation.rudder.facts.nodes.SelectFacts
import com.normation.rudder.reports.ReportingConfiguration
import com.normation.rudder.reports.execution.AgentRunWithNodeConfig
import com.normation.rudder.reports.execution.RoReportsExecutionRepository
@@ -110,7 +112,6 @@ import com.normation.rudder.services.reports.ReportingService
import com.normation.rudder.services.servers.DeleteMode
import com.normation.rudder.services.servers.NewNodeManager
import com.normation.rudder.services.servers.RemoveNodeService
-import com.normation.utils.Control._
import com.normation.utils.DateFormaterService
import com.normation.utils.StringUuidGenerator
import com.normation.zio._
@@ -127,7 +128,6 @@ import net.liftweb.common.Box
import net.liftweb.common.EmptyBox
import net.liftweb.common.Failure
import net.liftweb.common.Full
-import net.liftweb.common.Loggable
import net.liftweb.http.JsonResponse
import net.liftweb.http.LiftResponse
import net.liftweb.http.OutputStreamResponse
@@ -147,6 +147,7 @@ import org.joda.time.DateTime
import scalaj.http.Http
import scalaj.http.HttpOptions
import zio.{System => _, _}
+import zio.stream.ZSink
import zio.syntax._
/*
@@ -158,13 +159,7 @@ import zio.syntax._
class NodeApi(
restExtractorService: RestExtractorService,
serializer: RestDataSerializer,
- apiV2: NodeApiService2,
- apiV4: NodeApiService4,
- serviceV6: NodeApiService6,
- apiV8service: NodeApiService8,
- apiV12: NodeApiService12,
- apiV13: NodeApiService13,
- apiV15: NodeApiService15,
+ nodeApiService: NodeApiService,
inheritedProperties: NodeApiInheritedProperties,
deleteDefaultMode: DeleteMode
) extends LiftApiModuleProvider[API] {
@@ -215,7 +210,7 @@ class NodeApi(
nodes <- new String(json, StandardCharsets.UTF_8).fromJson[List[NodeDetails]].toIO
res <- ZIO.foldLeft(nodes)(ResultHolder(Nil, Nil)) {
case (res, node) =>
- apiV15.saveNode(node, authzToken.actor).either.map {
+ nodeApiService.saveNode(node, authzToken.actor, req.remoteAddr).either.map {
case Right(id) => res.modify(_.created).using(_ :+ id)
case Left(err) => res.modify(_.failed).using(_ :+ ((node.id, err)))
}
@@ -250,7 +245,7 @@ class NodeApi(
): LiftResponse = {
restExtractor.extractNodeDetailLevel(req.params) match {
case Full(level) =>
- apiV4.nodeDetailsGeneric(NodeId(id), level, version, req)
+ nodeApiService.nodeDetailsGeneric(NodeId(id), level, version, req)
case eb: EmptyBox =>
val failMsg = eb ?~ "node detail level not correctly sent"
toJsonError(None, failMsg.msg)("nodeDetail", params.prettify)
@@ -309,7 +304,7 @@ class NodeApi(
params: DefaultParams,
authzToken: AuthzToken
): LiftResponse = {
- apiV2.pendingNodeDetails(NodeId(id), params.prettify)
+ nodeApiService.pendingNodeDetails(NodeId(id), params.prettify)
}
}
@@ -340,7 +335,7 @@ class NodeApi(
.map(_.getOrElse(deleteDefaultMode))
.getOrElse(deleteDefaultMode)
- apiV12.deleteNode(NodeId(id), authzToken.actor, pretiffy, mode)
+ nodeApiService.deleteNode(NodeId(id), authzToken.actor, req.remoteAddr, pretiffy, mode)
}
}
@@ -365,7 +360,7 @@ class NodeApi(
restExtractor.extractNode(req.params)
}
reason <- restExtractor.extractReason(req)
- result <- apiV8service.updateRestNode(NodeId(id), restNode, authzToken.actor, reason).toBox
+ result <- nodeApiService.updateRestNode(NodeId(id), restNode, authzToken.actor, reason).toBox
} yield {
toJsonResponse(Some(id), serializer.serializeNode(result))
}) match {
@@ -392,7 +387,7 @@ class NodeApi(
} else {
(restExtractor.extractNodeIds(req.params), restExtractor.extractNodeStatus(req.params))
}
- apiV2.changeNodeStatus(nodeIds, nodeStatus, authzToken.actor, prettify)
+ nodeApiService.changeNodeStatus(nodeIds, nodeStatus, authzToken.actor, req.remoteAddr, prettify)
}
}
@@ -417,7 +412,7 @@ class NodeApi(
} else {
restExtractor.extractNodeStatus(req.params)
}
- apiV2.changeNodeStatus(Full(Some(List(NodeId(id)))), nodeStatus, authzToken.actor, prettify)
+ nodeApiService.changeNodeStatus(Full(Some(List(NodeId(id)))), nodeStatus, authzToken.actor, req.remoteAddr, prettify)
}
}
@@ -430,9 +425,9 @@ class NodeApi(
case Full(level) =>
restExtractor.extractQuery(req.params) match {
case Full(None) =>
- serviceV6.listNodes(AcceptedInventory, level, None, version)
+ nodeApiService.listNodes(AcceptedInventory, level, None, version)
case Full(Some(query)) =>
- serviceV6.queryNodes(query, AcceptedInventory, level, version)
+ nodeApiService.queryNodes(query, AcceptedInventory, level, version)
case eb: EmptyBox =>
val failMsg = eb ?~ "Node query not correctly sent"
toJsonError(None, failMsg.msg)("listAcceptedNodes", prettify)
@@ -454,9 +449,9 @@ class NodeApi(
case Full(level) =>
restExtractor.extractQuery(req.params) match {
case Full(None) =>
- serviceV6.listNodes(PendingInventory, level, None, version)
+ nodeApiService.listNodes(PendingInventory, level, None, version)
case Full(Some(query)) =>
- serviceV6.queryNodes(query, PendingInventory, level, version)
+ nodeApiService.queryNodes(query, PendingInventory, level, version)
case eb: EmptyBox =>
val failMsg = eb ?~ "Query for pending nodes not correctly sent"
toJsonError(None, failMsg.msg)("listPendingNodes", prettify)
@@ -477,7 +472,7 @@ class NodeApi(
(for {
classes <- restExtractorService.extractList("classes")(req)(json => Full(json))
- response <- apiV8service.runAllNodes(classes)
+ response <- nodeApiService.runAllNodes(classes)
} yield {
toJsonResponse(None, response)
}) match {
@@ -504,12 +499,12 @@ class NodeApi(
implicit val prettify = params.prettify
(for {
classes <- restExtractorService.extractList("classes")(req)(json => Full(json))
- optNode <- apiV2.nodeInfoService.getNodeInfo(NodeId(id)).toBox
+ optNode <- nodeApiService.nodeInfoService.getNodeInfo(NodeId(id)).toBox
} yield {
optNode match {
case Some(node)
if (node.agentsName.exists(a => a.agentType == AgentType.CfeCommunity || a.agentType == AgentType.CfeEnterprise)) =>
- OutputStreamResponse(apiV8service.runNode(node.id, classes))
+ OutputStreamResponse(nodeApiService.runNode(node.id, classes))
case Some(node) =>
toJsonError(
None,
@@ -542,9 +537,9 @@ class NodeApi(
ids <- (restExtractorService
.extractString("ids")(req)(ids => Full(ids.split(",").map(_.trim))))
.map(_.map(_.toList).getOrElse(Nil)) ?~! "Error: 'ids' parameter not found"
- accepted <- apiV2.nodeInfoService.getAllNodesIds().map(_.map(_.value)).toBox ?~! errorMsg(ids)
- pending <- apiV2.nodeInfoService.getPendingNodeInfos().map(_.keySet.map(_.value)).toBox ?~! errorMsg(ids)
- deleted <- apiV2.nodeInfoService.getDeletedNodeInfos().map(_.keySet.map(_.value)).toBox ?~! errorMsg(ids)
+ accepted <- nodeApiService.nodeInfoService.getAllNodesIds().map(_.map(_.value)).toBox ?~! errorMsg(ids)
+ pending <- nodeApiService.nodeInfoService.getPendingNodeInfos().map(_.keySet.map(_.value)).toBox ?~! errorMsg(ids)
+ deleted <- nodeApiService.nodeInfoService.getDeletedNodeInfos().map(_.keySet.map(_.value)).toBox ?~! errorMsg(ids)
} yield {
val array = ids.map { id =>
val status = {
@@ -576,7 +571,7 @@ class NodeApi(
val restExtractor = restExtractorService
def process0(version: ApiVersion, path: ApiPath, req: Req, params: DefaultParams, authzToken: AuthzToken): LiftResponse = {
(for {
- nodes <- apiV13.listNodes(req)
+ nodes <- nodeApiService.listNodes(req)
} yield {
JsonResponse(nodes)
}) match {
@@ -599,7 +594,7 @@ class NodeApi(
authzToken: AuthzToken
): LiftResponse = {
(for {
- response <- apiV13.software(req, software)
+ response <- nodeApiService.software(req, software)
} yield {
response
}) match {
@@ -629,7 +624,7 @@ class NodeApi(
): LiftResponse = {
(for {
inheritedProperty <- req.json.flatMap(j => OptionnalJson.extractJsonBoolean(j, "inherited"))
- response <- apiV13.property(req, property, inheritedProperty.getOrElse(false))
+ response <- nodeApiService.property(req, property, inheritedProperty.getOrElse(false))
} yield {
response
}) match {
@@ -677,38 +672,34 @@ class NodeApiInheritedProperties(
}
}
-class NodeApiService12(
- removeNodeService: RemoveNodeService,
- uuidGen: StringUuidGenerator,
- restSerializer: RestDataSerializer
+class NodeApiService(
+ ldapConnection: LDAPConnectionProvider[RwLDAPConnection],
+ nodeFactRepository: NodeFactRepository,
+ inventoryRepository: NodeFactFullInventoryRepositoryProxy,
+ groupRepo: RoNodeGroupRepository,
+ paramRepo: RoParameterRepository,
+ reportsExecutionRepository: RoReportsExecutionRepository,
+ nodeRepository: WoNodeRepository,
+ ldapEntityMapper: LDAPEntityMapper,
+ uuidGen: StringUuidGenerator,
+ nodeDit: NodeDit,
+ pendingDit: InventoryDit,
+ acceptedDit: InventoryDit,
+ val nodeInfoService: NodeInfoService,
+ newNodeManager: NewNodeManager,
+ removeNodeService: RemoveNodeService,
+ restExtractor: RestExtractorService,
+ restSerializer: RestDataSerializer,
+ reportingService: ReportingService,
+ acceptedNodeQueryProcessor: QueryProcessor,
+ pendingNodeQueryProcessor: QueryChecker,
+ asyncRegenerate: AsyncDeploymentActor,
+ userService: UserService,
+ getGlobalMode: () => Box[GlobalPolicyMode],
+ relayApiEndpoint: String
) {
- def deleteNode(id: NodeId, actor: EventActor, prettify: Boolean, mode: DeleteMode) = {
- implicit val p = prettify
- implicit val action = "deleteNode"
- val modId = ModificationId(uuidGen.newUuid)
-
- removeNodeService.removeNodePure(id, mode, modId, actor).toBox match {
- case Full(info) =>
- toJsonResponse(None, ("nodes" -> JArray(restSerializer.serializeNodeInfo(info, "deleted") :: Nil)))
-
- case eb: EmptyBox =>
- val message = (eb ?~ ("Error when deleting Nodes")).msg
- toJsonError(None, message)
- }
- }
-}
-class NodeApiService15(
- inventoryRepos: LDAPFullInventoryRepository,
- ldapConnection: LDAPConnectionProvider[RwLDAPConnection],
- ldapEntityMapper: LDAPEntityMapper,
- newNodeManager: NewNodeManager,
- uuidGen: StringUuidGenerator,
- nodeDit: NodeDit,
- pendingDit: InventoryDit,
- acceptedDit: InventoryDit
-) {
- /// utility functions ///
+/// utility functions ///
/*
* for a given nodedetails, we:
@@ -717,7 +708,7 @@ class NodeApiService15(
* - if needed, accept
* - now, setup node info (property, state, etc)
*/
- def saveNode(nodeDetails: Rest.NodeDetails, eventActor: EventActor): IO[CreationError, NodeId] = {
+ def saveNode(nodeDetails: Rest.NodeDetails, eventActor: EventActor, actorIp: String): IO[CreationError, NodeId] = {
def toCreationError(res: ValidatedNel[NodeValidationError, NodeTemplate]) = {
res match {
case Invalid(nel) => CreationError.OnValidation(nel).fail
@@ -729,7 +720,7 @@ class NodeApiService15(
validated <- toCreationError(Validation.toNodeTemplate(nodeDetails))
_ <- checkUuid(validated.inventory.node.main.id)
created <- saveInventory(validated.inventory)
- nodeSetup <- accept(validated, eventActor)
+ nodeSetup <- accept(validated, eventActor, actorIp)
nodeId <- saveRudderNode(validated.inventory.node.main.id, nodeSetup)
} yield {
nodeId
@@ -769,21 +760,20 @@ class NodeApiService15(
* is done afterward if needed.
*/
def saveInventory(inventory: FullInventory): IO[CreationError, NodeId] = {
- inventoryRepos
+ inventoryRepository
.save(inventory)
.map(_ => inventory.node.main.id)
.mapError(err => CreationError.OnSaveInventory(s"Error during node creation: ${err.fullMsg}"))
}
- def accept(template: NodeTemplate, eventActor: EventActor): IO[CreationError, NodeSetup] = {
+ def accept(template: NodeTemplate, eventActor: EventActor, actorIp: String): IO[CreationError, NodeSetup] = {
val id = template.inventory.node.main.id
// only nodes with status "accepted" need to be accepted
template match {
case AcceptedNodeTemplate(_, properties, policyMode, state) =>
newNodeManager
- .accept(id, ModificationId(uuidGen.newUuid), eventActor)
- .toIO
+ .accept(id)(ChangeContext(ModificationId(uuidGen.newUuid), eventActor, DateTime.now(), None, Some(actorIp)))
.mapError(err => CreationError.OnAcceptation((s"Can not accept node '${id.value}': ${err.fullMsg}"))) *>
NodeSetup(properties, policyMode, state).succeed
case PendingNodeTemplate(_, properties) =>
@@ -836,18 +826,6 @@ class NodeApiService15(
merged.id
}).mapError(err => CreationError.OnSaveNode(s"Error during node creation: ${err.fullMsg}"))
}
-}
-
-class NodeApiService13(
- nodeInfoService: NodeInfoService,
- reportsExecutionRepository: RoReportsExecutionRepository,
- readOnlySoftwareDAO: ReadOnlySoftwareDAO,
- restExtractor: RestExtractorService,
- getGlobalMode: () => Box[GlobalPolicyMode],
- reportingService: ReportingService,
- groupRepo: RoNodeGroupRepository,
- paramRepo: RoParameterRepository
-) extends Loggable {
/*
* Return a map of (NodeId -> propertyName -> inherited property) for the given list of nodes and
@@ -994,7 +972,7 @@ class NodeApiService13(
_ = TimingDebugLoggerPure.logEffect.trace(s"Getting global mode: ${n5 - n4}ms")
softToLookAfter <- req.json.flatMap(j => OptionnalJson.extractJsonListString(j, "software").map(_.getOrElse(Nil)))
softs <- ZIO
- .foreach(softToLookAfter)(soft => readOnlySoftwareDAO.getNodesbySofwareName(soft))
+ .foreach(softToLookAfter)(soft => inventoryRepository.getNodesbySofwareName(soft))
.toBox
.map(_.flatten.groupMap(_._1)(_._2))
n6 = System.currentTimeMillis
@@ -1064,7 +1042,7 @@ class NodeApiService13(
case None => nodeInfoService.getAll().toBox
case Some(nodeIds) => nodeInfoService.getNodeInfosSeq(nodeIds).map(_.map(n => (n.id, n)).toMap).toBox
}
- softs <- readOnlySoftwareDAO.getNodesbySofwareName(software).toBox.map(_.toMap)
+ softs <- inventoryRepository.getNodesbySofwareName(software).toBox.map(_.toMap)
} yield {
JsonResponse(
JObject(nodes.keySet.toList.flatMap(id => softs.get(id).flatMap(_.version.map(v => JField(id.value, JString(v.value))))))
@@ -1099,16 +1077,6 @@ class NodeApiService13(
JsonResponse(JObject(nodes.keySet.toList.flatMap(id => mapProps.get(id).toList.flatMap(_.map(p => JField(id.value, p))))))
}
}
-}
-
-class NodeApiService2(
- newNodeManager: NewNodeManager,
- val nodeInfoService: NodeInfoService,
- removeNodeService: RemoveNodeService,
- uuidGen: StringUuidGenerator,
- restExtractor: RestExtractorService,
- restSerializer: RestDataSerializer
-) extends Loggable {
import restSerializer._
def listAcceptedNodes(req: Req) = {
@@ -1143,14 +1111,14 @@ class NodeApiService2(
def pendingNodeDetails(nodeId: NodeId, prettifyStatus: Boolean) = {
implicit val prettify = prettifyStatus
implicit val action = "pendingNodeDetails"
- newNodeManager.listNewNodes match {
+ newNodeManager.listNewNodes.toBox match {
case Full(pendingNodes) =>
pendingNodes.filter(_.id == nodeId) match {
case Seq() =>
val message = s"Could not find pending Node ${nodeId.value}"
toJsonError(None, message)
case Seq(info) =>
- val node = serializeServerInfo(info, "pending")
+ val node = serializeServerInfo(info.toSrv, "pending")
toJsonResponse(None, ("nodes" -> JArray(List(node))))
case tooManyNodes =>
val message = s"Too many pending Nodes with same id ${nodeId.value} : ${tooManyNodes.size} "
@@ -1165,9 +1133,9 @@ class NodeApiService2(
def listPendingNodes(req: Req) = {
implicit val prettify = restExtractor.extractPrettify(req.params)
implicit val action = "listPendingNodes"
- newNodeManager.listNewNodes match {
- case Full(ids) =>
- val pendingNodes = ids.map(serializeServerInfo(_, "pending")).toList
+ newNodeManager.listNewNodes.toBox match {
+ case Full(cnfs) =>
+ val pendingNodes = cnfs.map(cnf => serializeServerInfo(cnf.toSrv, "pending")).toList
toJsonResponse(None, ("nodes" -> JArray(pendingNodes)))
case eb: EmptyBox =>
@@ -1177,38 +1145,41 @@ class NodeApiService2(
}
def modifyStatusFromAction(
- ids: Seq[NodeId],
- action: NodeStatusAction,
- modId: ModificationId,
- actor: EventActor
- ): Box[List[JValue]] = {
- def actualNodeDeletion(id: NodeId, modId: ModificationId, actor: EventActor) = {
+ ids: Seq[NodeId],
+ action: NodeStatusAction
+ )(implicit cc: ChangeContext): Box[List[JValue]] = {
+ def actualNodeDeletion(id: NodeId)(implicit cc: ChangeContext) = {
for {
optInfo <- nodeInfoService.getNodeInfo(id).toBox
info <- optInfo match {
case None => Failure(s"Can not removed the node with id '${id.value}' because it was not found")
case Some(x) => Full(x)
}
- remove <- removeNodeService.removeNode(info.id, modId, actor)
+ remove <- removeNodeService.removeNode(info.id)
} yield { serializeNodeInfo(info, "deleted") }
}
(action match {
case AcceptNode =>
- newNodeManager.accept(ids, modId, actor, "").map(_.map(serializeInventory(_, "accepted")))
+ newNodeManager
+ .acceptAll(ids)
+ .map(_.map(cnf => serializeInventory(NodeFact.fromMinimal(cnf).toFullInventory, "accepted")))
case RefuseNode =>
- newNodeManager.refuse(ids, modId, actor, "").map(_.map(serializeServerInfo(_, "refused")))
+ newNodeManager
+ .refuseAll(ids)
+ .map(_.map(cnf => serializeServerInfo(cnf.toSrv, "refused")))
case DeleteNode =>
- boxSequence(ids.map(actualNodeDeletion(_, modId, actor)))
- }).map(_.toList)
+ ZIO.foreach(ids)(actualNodeDeletion(_).toIO)
+ }).toBox.map(_.toList)
}
def changeNodeStatus(
nodeIds: Box[Option[List[NodeId]]],
nodeStatusAction: Box[NodeStatusAction],
actor: EventActor,
+ actorIp: String,
prettifyStatus: Boolean
) = {
implicit val prettify = prettifyStatus
@@ -1219,7 +1190,9 @@ class NodeApiService2(
NodeLogger.PendingNode.debug(s" Nodes to change Status : ${ids.mkString("[ ", ", ", " ]")}")
nodeStatusAction match {
case Full(nodeStatusAction) =>
- modifyStatusFromAction(ids, nodeStatusAction, modId, actor) match {
+ modifyStatusFromAction(ids, nodeStatusAction)(
+ ChangeContext(modId, actor, DateTime.now(), None, Some(actorIp))
+ ) match {
case Full(result) =>
toJsonResponse(None, ("nodes" -> JArray(result)))
case eb: EmptyBox =>
@@ -1240,66 +1213,29 @@ class NodeApiService2(
}
}
-}
-
-class NodeApiService4(
- inventoryRepository: LDAPFullInventoryRepository,
- nodeInfoService: NodeInfoService,
- softwareRepository: ReadOnlySoftwareDAO,
- uuidGen: StringUuidGenerator,
- restExtractor: RestExtractorService,
- restSerializer: RestDataSerializer,
- roAgentRunsRepository: RoReportsExecutionRepository
-) extends Loggable {
-
- import restSerializer._
-
def getNodeDetails(
nodeId: NodeId,
detailLevel: NodeDetailLevel,
- state: InventoryStatus,
- version: ApiVersion
+ state: InventoryStatus
): IOResult[Option[JValue]] = {
for {
- optNodeInfo <- state match {
- case AcceptedInventory => nodeInfoService.getNodeInfo(nodeId)
- case PendingInventory => nodeInfoService.getPendingNodeInfo(nodeId)
- case RemovedInventory => nodeInfoService.getDeletedNodeInfo(nodeId)
- }
+ optNodeInfo <- nodeFactRepository.slowGetCompat(nodeId, state, SelectFacts.fromNodeDetailLevel(detailLevel))
nodeInfo <- optNodeInfo match {
case None => None.succeed
case Some(x) =>
for {
- runs <- roAgentRunsRepository.getNodesLastRun(Set(nodeId))
- inventory <- if (detailLevel.needFullInventory()) {
- inventoryRepository.get(nodeId, state)
- } else {
- None.succeed
- }
- software <- if (detailLevel.needSoftware()) {
- for {
- software <- inventory match {
- case Some(i) => softwareRepository.getSoftware(i.node.softwareIds)
- case None =>
- softwareRepository
- .getSoftwareByNode(Set(nodeId), state)
- .map(_.get(nodeId).getOrElse(Seq()))
- }
- } yield {
- software
- }
- } else {
- Seq().succeed
- }
+ runs <- reportsExecutionRepository.getNodesLastRun(Set(nodeId))
+ inventory = x.toFullInventory
+ software = x.software.toList.map(_.toSoftware)
} yield {
- Some((x, runs, inventory, software))
+ Some((x.toNodeInfo, runs, inventory, software))
}
}
} yield {
nodeInfo.map {
case (node, runs, inventory, software) =>
val runDate = runs.get(nodeId).flatMap(_.map(_.agentRunId.date))
- serializeInventory(node, state, runDate, inventory, software, detailLevel)
+ serializeInventory(node, state, runDate, Some(inventory), software, detailLevel)
}
}
}
@@ -1313,7 +1249,7 @@ class NodeApiService4(
) = {
implicit val prettify = restExtractor.extractPrettify(req.params)
implicit val action = s"${state.name}NodeDetails"
- getNodeDetails(nodeId, detailLevel, state, version).either.runNow match {
+ getNodeDetails(nodeId, detailLevel, state).either.runNow match {
case Right(Some(inventory)) =>
toJsonResponse(Some(nodeId.value), ("nodes" -> JArray(List(inventory))))
case Right(None) =>
@@ -1334,14 +1270,14 @@ class NodeApiService4(
implicit val prettify = restExtractor.extractPrettify(req.params)
implicit val action = "nodeDetails"
(for {
- accepted <- getNodeDetails(nodeId, detailLevel, AcceptedInventory, version)
+ accepted <- getNodeDetails(nodeId, detailLevel, AcceptedInventory)
orPending <- accepted match {
case Some(i) => Some(i).succeed
- case None => getNodeDetails(nodeId, detailLevel, PendingInventory, version)
+ case None => getNodeDetails(nodeId, detailLevel, PendingInventory)
}
orDeleted <- orPending match {
case Some(i) => Some(i).succeed
- case None => getNodeDetails(nodeId, detailLevel, RemovedInventory, version)
+ case None => getNodeDetails(nodeId, detailLevel, RemovedInventory)
}
} yield {
orDeleted match {
@@ -1363,80 +1299,43 @@ class NodeApiService4(
toJsonError(Some(nodeId.value), msg)
}
}
-}
-
-class NodeApiService6(
- nodeInfoService: NodeInfoService,
- inventoryRepository: LDAPFullInventoryRepository,
- softwareRepository: ReadOnlySoftwareDAO,
- restExtractor: RestExtractorService,
- restSerializer: RestDataSerializer,
- acceptedNodeQueryProcessor: QueryProcessor,
- pendingNodeQueryProcessor: QueryChecker,
- roAgentRunsRepository: RoReportsExecutionRepository
-) extends Loggable {
- import restSerializer._
def listNodes(state: InventoryStatus, detailLevel: NodeDetailLevel, nodeFilter: Option[Seq[NodeId]], version: ApiVersion)(
implicit prettify: Boolean
) = {
implicit val action = s"list${state.name.capitalize}Nodes"
+ val predicate = (n: NodeFact) => {
+ (nodeFilter match {
+ case Some(ids) => ids.contains(n.id)
+ case None => true
+ })
+ }
(for {
- nodeInfos <- state match {
- case AcceptedInventory => nodeInfoService.getAll()
- case PendingInventory => nodeInfoService.getPendingNodeInfos()
- case RemovedInventory => nodeInfoService.getDeletedNodeInfos()
- }
- nodeIds = nodeFilter.getOrElse(nodeInfos.keySet).toSet
- runs <- roAgentRunsRepository.getNodesLastRun(nodeIds)
- inventories <- if (detailLevel.needFullInventory()) {
- for {
- d1 <- currentTimeMillis
- // the request with filter if quite complex, and just sending all the node DN for it to LDAP produces
- // a lot of data transfer. So a some point, it's better to just get everything back and filter out
- // data below, trading ram for LDAP processing. No idea what a good heuristic would be, setting
- // it to 2/3
- res <- (if (nodeIds.size < nodeInfos.size * 2 / 3) {
- inventoryRepository.getInventories(state, nodeIds)
- } else {
- inventoryRepository.getAllInventories(state)
- }).chainError("Error when looking for node inventories")
- d2 <- currentTimeMillis
- _ <- ApiLoggerPure.Metrics.debug(s"[${d2 - d1} ms] Getting inventories for level '${detailLevel}' ")
- } yield res
- } else {
- Map[NodeId, FullInventory]().succeed
- }
- software <- if (detailLevel.needSoftware()) {
- for {
- d1 <- currentTimeMillis
- res <- softwareRepository.getSoftwareByNode(nodeIds, state)
- d2 <- currentTimeMillis
- _ <- ApiLoggerPure.Metrics.debug(s"[${d2 - d1} ms] Getting software for level '${detailLevel}'")
- } yield res
- } else {
- Map[NodeId, Seq[Software]]().succeed
- }
- d1 <- currentTimeMillis
- jsons = nodeIds.flatMap { id =>
- nodeInfos
- .get(id)
- .map { nodeInfo =>
- serializeInventory(
- nodeInfo,
- state,
- runs.get(id).flatMap(_.map(_.agentRunId.date)),
- inventories.get(id),
- software.getOrElse(id, Seq()),
- detailLevel
- )
- }
- }
- d2 <- currentTimeMillis
- _ <- ApiLoggerPure.Metrics.debug(s"[${d2 - d1} ms] Serializing nodes to json values")
+ nodeFacts <-
+ nodeFactRepository
+ .slowGetAllCompat(state, SelectFacts.fromNodeDetailLevel(detailLevel))
+ .filter(predicate)
+ .run(ZSink.collectAllToMap[NodeFact, NodeId](_.id)((a, b) => a))
+ nodeIds = nodeFacts.keySet
+ runs <- reportsExecutionRepository.getNodesLastRun(nodeIds)
+ inventories = nodeFacts.map { case (k, v) => (k, v.toFullInventory) }
+ software = nodeFacts.map { case (k, v) => (k, v.software.map(_.toSoftware)) }
} yield {
- jsons
+ for {
+ nodeId <- nodeIds
+ nodeFact <- nodeFacts.get(nodeId)
+ } yield {
+ val runDate = runs.get(nodeId).flatMap(_.map(_.agentRunId.date))
+ serializeInventory(
+ nodeFact.toNodeInfo,
+ state,
+ runDate,
+ inventories.get(nodeId),
+ software.getOrElse(nodeId, Seq()),
+ detailLevel
+ )
+ }
}).either.runNow match {
case Right(nodes) => {
toJsonResponse(None, ("nodes" -> JArray(nodes.toList)))
@@ -1474,17 +1373,6 @@ class NodeApiService6(
}
}
-}
-
-class NodeApiService8(
- nodeRepository: WoNodeRepository,
- nodeInfoService: NodeInfoService,
- uuidGen: StringUuidGenerator,
- asyncRegenerate: AsyncDeploymentActor,
- relayApiEndpoint: String,
- userService: UserService
-) extends Loggable {
-
def updateRestNode(nodeId: NodeId, restNode: RestNode, actor: EventActor, reason: Option[String]): IOResult[Node] = {
val modId = ModificationId(uuidGen.newUuid)
@@ -1691,4 +1579,19 @@ class NodeApiService8(
}
}
+ def deleteNode(id: NodeId, actor: EventActor, actorIp: String, prettify: Boolean, mode: DeleteMode) = {
+ implicit val p = prettify
+ implicit val action = "deleteNode"
+ val modId = ModificationId(uuidGen.newUuid)
+
+ removeNodeService.removeNodePure(id, mode)(ChangeContext(modId, actor, DateTime.now(), None, Some(actorIp))).toBox match {
+ case Full(info) =>
+ toJsonResponse(None, ("nodes" -> JArray(restSerializer.serializeNodeInfo(info, "deleted") :: Nil)))
+
+ case eb: EmptyBox =>
+ val message = (eb ?~ ("Error when deleting Nodes")).msg
+ toJsonError(None, message)
+ }
+ }
+
}
diff --git a/webapp/sources/rudder/rudder-rest/src/test/resources/api/api_nodes.yml b/webapp/sources/rudder/rudder-rest/src/test/resources/api/api_nodes.yml
index 9b52479da01..d095d74d5da 100644
--- a/webapp/sources/rudder/rudder-rest/src/test/resources/api/api_nodes.yml
+++ b/webapp/sources/rudder/rudder-rest/src/test/resources/api/api_nodes.yml
@@ -73,25 +73,25 @@ response:
{"name":"swap","totalSpace":0},
{"name":"ext4","freeSpace":11517,"totalSpace":52524,"mountPoint":"/"}
],
- "managementTechnologyDetails":{"cfengineKeys":[],"cfengineUser":"root"},
+ "managementTechnologyDetails":{
+ "cfengineKeys":[
+ "-----BEGINRSAPUBLICKEY-----\nMIIBCAKCAQEAlntroa72gD50MehPoyp6mRS5fzZpsZEHu42vq9KKxbqSsjfUmxnT\nRsi8CDvBt7DApIc7W1g0eJ6AsOfV7CEh3ooiyL/fC9SGATyDg5TjYPJZn3MPUktg\nYBzTd1MMyZL6zcLmIpQBH6XHkH7Do/RxFRtaSyicLxiO3H3wapH20TnkUvEpV5Qh\nzUkNM8vHZuu3m1FgLrK5NCN7BtoGWgeyVJvBMbWww5hS15IkCRuBkAOK/+h8xe2f\nhMQjrt9gW2qJpxZyFoPuMsWFIaX4wrN7Y8ZiN37U2q1G11tv2oQlJTQeiYaUnTX4\nz5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==\n-----ENDRSAPUBLICKEY-----"
+ ],
+ "cfengineUser":"root"
+ },
"networkInterfaces":[{"name":"enp0s3","mask":[],"speed":"1000","status":"Up","ipAddresses":["10.0.2.15"]}],
"processes":[{"pid":54432,"name":"/bin/true","memory":4235,"cpuUsage":34.5}],
"software":[
- {"name":"s09","version":"1.0"},
- {"name":"s06","version":"1.0"},
{"name":"s05","version":"1.0"},
- {"name":"s13","version":"1.0"},
+ {"name":"s06","version":"1.0"},
+ {"name":"s07","version":"1.0"},
{"name":"s08","version":"1.0"},
- {"name":"s02","version":"1.0"},
- {"name":"s03","version":"1.0"},
- {"name":"s04","version":"1.0"},
+ {"name":"s09","version":"1.0"},
{"name":"s10","version":"1.0"},
{"name":"s11","version":"1.0"},
{"name":"s12","version":"1.0"},
- {"name":"s01","version":"1.0"},
- {"name":"s00","version":"1.0"},
- {"name":"s07","version":"1.0"}
- ],
+ {"name":"s13","version":"1.0"}
+ ],
"softwareUpdate":[
{"name":"s00","version":"2.15.6~RC1","arch":"x86_64","from":"yum","kind":"defect","description":"Some explanation","severity":"critical","ids":["RHSA-2020-4566","CVE-2021-4034"]},
{"name":"s01","version":"1-23-RELEASE-1","arch":"x86_64","from":"apt","kind":"none","source":"default-repo"},
@@ -117,7 +117,12 @@ response:
"id":"node1",
"hostname":"node1.localhost",
"status":"accepted",
- "managementTechnologyDetails":{"cfengineKeys":[],"cfengineUser":"root"}
+ "managementTechnologyDetails":{
+ "cfengineKeys":[
+ "-----BEGINRSAPUBLICKEY-----\nMIIBCAKCAQEAlntroa72gD50MehPoyp6mRS5fzZpsZEHu42vq9KKxbqSsjfUmxnT\nRsi8CDvBt7DApIc7W1g0eJ6AsOfV7CEh3ooiyL/fC9SGATyDg5TjYPJZn3MPUktg\nYBzTd1MMyZL6zcLmIpQBH6XHkH7Do/RxFRtaSyicLxiO3H3wapH20TnkUvEpV5Qh\nzUkNM8vHZuu3m1FgLrK5NCN7BtoGWgeyVJvBMbWww5hS15IkCRuBkAOK/+h8xe2f\nhMQjrt9gW2qJpxZyFoPuMsWFIaX4wrN7Y8ZiN37U2q1G11tv2oQlJTQeiYaUnTX4\nz5VEb9yx2KikbWyChM1Akp82AV5BzqE80QIBIw==\n-----ENDRSAPUBLICKEY-----"
+ ],
+ "cfengineUser":"root"
+ }
}
]
}
diff --git a/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/NodeApiTest.scala b/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/NodeApiTest.scala
index f6ad65503d6..263125276e1 100644
--- a/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/NodeApiTest.scala
+++ b/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/NodeApiTest.scala
@@ -84,13 +84,13 @@ class NodeApiTest extends Specification with Loggable {
resp match {
case Full(JsonResponsePrettify(content, _, _, 200, _)) =>
val jsonString = compactRender(content)
- val nodes = restTestSetUp.mockNodes.nodeInfoService.nodeBase.get.runNow
+ val nodes = restTestSetUp.mockNodes.nodeFactStorage.nodeFactBase.get.runNow
val n1 = nodes.get(NodeId(node1)).getOrElse(throw new IllegalArgumentException("error: node1"))
val n2 = nodes.get(NodeId(node2)).getOrElse(throw new IllegalArgumentException("error: node2"))
(jsonString must beEqualTo(jsonRes)) and
- (n1.nInv.main.status must beEqualTo(PendingInventory)) and
- (n2.nInv.main.status must beEqualTo(AcceptedInventory))
+ (n1.rudderSettings.status must beEqualTo(PendingInventory)) and
+ (n2.rudderSettings.status must beEqualTo(AcceptedInventory))
case _ => ko("unexpected answer")
}
diff --git a/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/RestTestSetUp.scala b/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/RestTestSetUp.scala
index 9c79eb0a6cd..1df345b4a52 100644
--- a/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/RestTestSetUp.scala
+++ b/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/RestTestSetUp.scala
@@ -50,7 +50,6 @@ import com.normation.eventlog.EventLogFilter
import com.normation.eventlog.ModificationId
import com.normation.inventory.domain.FullInventory
import com.normation.inventory.domain.NodeId
-import com.normation.inventory.domain.NodeInventory
import com.normation.rudder._
import com.normation.rudder.api.{ApiAuthorization => ApiAuthz}
import com.normation.rudder.api.ApiVersion
@@ -79,6 +78,10 @@ import com.normation.rudder.domain.reports.NodeExpectedReports
import com.normation.rudder.domain.reports.NodeModeConfig
import com.normation.rudder.domain.secret.Secret
import com.normation.rudder.domain.workflows.ChangeRequestId
+import com.normation.rudder.facts.nodes.ChangeContext
+import com.normation.rudder.facts.nodes.CoreNodeFact
+import com.normation.rudder.facts.nodes.NodeFact
+import com.normation.rudder.facts.nodes.SelectFacts
import com.normation.rudder.git.GitArchiveId
import com.normation.rudder.git.GitCommitId
import com.normation.rudder.git.GitPath
@@ -109,7 +112,6 @@ import com.normation.rudder.services.healthcheck.CheckFreeSpace
import com.normation.rudder.services.healthcheck.HealthcheckNotificationService
import com.normation.rudder.services.healthcheck.HealthcheckService
import com.normation.rudder.services.marshalling.DeploymentStatusSerialisation
-import com.normation.rudder.services.nodes.NodeInfoServiceCachedImpl
import com.normation.rudder.services.policies.DependencyAndDeletionServiceImpl
import com.normation.rudder.services.policies.FindDependencies
import com.normation.rudder.services.policies.InterpolationContext
@@ -289,7 +291,6 @@ class RestTestSetUp {
override def getDirectiveLibrary(ids: Set[DirectiveId]): Box[FullActiveTechniqueCategory] = ???
override def getGroupLibrary(): Box[FullNodeGroupCategory] = ???
override def getAllGlobalParameters: Box[Seq[GlobalParameter]] = ???
- override def getAllInventories(): Box[Map[NodeId, NodeInventory]] = ???
override def getGlobalComplianceMode(): Box[GlobalComplianceMode] = ???
override def getGlobalAgentRun(): Box[AgentRunInterval] = ???
override def getScriptEngineEnabled: () => Box[FeatureSwitch] = ???
@@ -580,16 +581,6 @@ class RestTestSetUp {
val fakeScriptLauncher = new DebugInfoService {
override def launch() = DebugInfoScriptResult("test", new Array[Byte](42)).succeed
}
- val nodeInfoService = new NodeInfoServiceCachedImpl(
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- FiniteDuration(100, "millis")
- )
val fakeUpdateDynamicGroups = {
new UpdateDynamicGroups(dynGroupService, dynGroupUpdaterService, asyncDeploymentAgent, uuidGen, 1, () => Full("1")) {
@@ -616,7 +607,7 @@ class RestTestSetUp {
List(
CheckCoreNumber,
CheckFreeSpace,
- new CheckFileDescriptorLimit(nodeInfoService) // should I create all services to get this one ?
+ new CheckFileDescriptorLimit(mockNodes.nodeInfoService)
)
)
val fakeHcNotifService = new HealthcheckNotificationService(fakeHealthcheckService, 5.minute)
@@ -732,7 +723,6 @@ class RestTestSetUp {
val authzToken = AuthzToken(EventActor("fakeToken"))
val systemStatusPath = "api" + systemApi.Status.schema.path
- val nodeInfo = mockNodes.nodeInfoService
val softDao = mockNodes.softwareDao
val roReportsExecutionRepository = new RoReportsExecutionRepository {
override def getNodesLastRun(nodeIds: Set[NodeId]): IOResult[Map[NodeId, Option[AgentRunWithNodeConfig]]] =
@@ -743,43 +733,37 @@ class RestTestSetUp {
def getUnprocessedRuns(): IOResult[Seq[AgentRunWithoutCompliance]] = ???
}
- val nodeApiService2 = new NodeApiService2(null, nodeInfo, null, uuidGen, restExtractorService, restDataSerializer)
- val nodeApiService4 = new NodeApiService4(
- nodeInfo,
- nodeInfo,
- softDao,
+ val nodeApiService = new NodeApiService(
+ null,
+ mockNodes.nodeFactRepo,
+ mockNodes.fullInventoryRepository,
+ null,
+ null,
+ roReportsExecutionRepository,
+ mockNodes.woNodeRepository,
+ null,
uuidGen,
+ null,
+ null,
+ null,
+ mockNodes.nodeInfoService,
+ mockNodes.newNodeManager,
+ null,
restExtractorService,
restDataSerializer,
- roReportsExecutionRepository
- )
- val nodeApiService6 = new NodeApiService6(
- nodeInfo,
- nodeInfo,
- softDao,
- restExtractorService,
- restDataSerializer,
+ null,
mockNodes.queryProcessor,
null,
- roReportsExecutionRepository
- )
- val nodeApiService8 = new NodeApiService8(nodeInfo, nodeInfo, uuidGen, asyncDeploymentAgent, "relay", userService)
- val nodeApiService12 = new NodeApiService12(null, uuidGen, restDataSerializer)
- val nodeApiService13 = new NodeApiService13(
- nodeInfo,
- roReportsExecutionRepository,
- softDao,
- restExtractorService,
+ asyncDeploymentAgent,
+ userService,
() => Full(GlobalPolicyMode(Audit, PolicyModeOverrides.Always)),
- null,
- null,
- null
- )
- // override ldap methods to use mock nodes
- val nodeApiService16 = new NodeApiService15(nodeInfo, null, null, mockNodes.newNodeManager, uuidGen, null, null, null) {
+ "relay"
+ ) {
+ implicit val testCC: ChangeContext =
+ ChangeContext(ModificationId(uuidGen.newUuid), EventActor("test"), DateTime.now(), None, None)
override def checkUuid(nodeId: NodeId): IO[Creation.CreationError, Unit] = {
- mockNodes.nodeInfoService
+ mockNodes.nodeFactRepo
.get(nodeId)
.map(_.nonEmpty)
.mapError(err => CreationError.OnSaveInventory(s"Error during node ID check: ${err.fullMsg}"))
@@ -787,8 +771,8 @@ class RestTestSetUp {
}
override def saveInventory(inventory: FullInventory): IO[Creation.CreationError, NodeId] = {
- mockNodes.nodeInfoService
- .save(inventory)
+ mockNodes.nodeFactRepo
+ .updateInventory(inventory, None)
.mapBoth(
err => CreationError.OnSaveInventory(s"Error when saving node: ${err.fullMsg}"),
_ => inventory.node.main.id
@@ -796,16 +780,13 @@ class RestTestSetUp {
}
override def saveRudderNode(id: NodeId, setup: NodeSetup): IO[Creation.CreationError, NodeId] = {
- mockNodes.nodeInfoService.nodeBase.updateZIO { nodes =>
- nodes.get(id) match {
- case None => CreationError.OnSaveNode(s"Can not merge node: missing").fail
- case Some(n) =>
- import com.softwaremill.quicklens._
- val res = n.modify(_.info.node).using(x => mergeNodeSetup(x, setup))
-
- (nodes + ((id, res))).succeed
- }
- }.map(_ => id)
+ (for {
+ n <- mockNodes.nodeFactRepo.get(id).notOptional(s"Can not merge node: missing")
+ n2 = CoreNodeFact.updateNode(n, mergeNodeSetup(n.toNode, setup))
+ _ <- mockNodes.nodeFactRepo.save(NodeFact.fromMinimal(n2))(testCC, SelectFacts.none)
+ } yield {
+ n.id
+ }).mapError(err => CreationError.OnSaveInventory(err.fullMsg))
}
}
@@ -920,13 +901,7 @@ class RestTestSetUp {
new NodeApi(
restExtractorService,
restDataSerializer,
- nodeApiService2,
- nodeApiService4,
- nodeApiService6,
- nodeApiService8,
- nodeApiService12,
- nodeApiService13,
- nodeApiService16,
+ nodeApiService,
null,
DeleteMode.Erase
),
@@ -946,7 +921,7 @@ class RestTestSetUp {
asyncDeploymentAgent,
uuidGen,
settingsService.policyServerManagementService,
- nodeInfo
+ mockNodes.nodeInfoService
),
archiveAPIModule.api,
campaignApiModule.api
diff --git a/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/TestRestPlusInPath.scala b/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/TestRestPlusInPath.scala
index a36910744ab..5b77cd36534 100644
--- a/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/TestRestPlusInPath.scala
+++ b/webapp/sources/rudder/rudder-rest/src/test/scala/com/normation/rudder/rest/TestRestPlusInPath.scala
@@ -37,10 +37,9 @@
package com.normation.rudder.rest
-import com.normation.inventory.domain.AcceptedInventory
-import com.normation.inventory.domain.NodeInventory
-import com.normation.inventory.domain.NodeSummary
-import com.normation.rudder.NodeDetails
+import com.normation.GitVersion.Revision
+import com.normation.rudder.domain.policies.RuleId
+import com.normation.rudder.domain.policies.RuleUid
import com.normation.zio._
import net.liftweb.common.Full
import net.liftweb.http.InMemoryResponse
@@ -49,7 +48,6 @@ import org.junit.runner.RunWith
import org.specs2.mutable._
import org.specs2.runner.JUnitRunner
import org.specs2.specification.BeforeAfterAll
-import zio.syntax._
// test that the "+" in path is correctly kept as a "+", not changed into " "
// See: https://issues.rudder.io/issues/20943
@@ -62,30 +60,20 @@ class TestRestPlusInPath extends Specification with BeforeAfterAll {
.getLogger("com.normation.rudder.rest.RestUtils")
.asInstanceOf[ch.qos.logback.classic.Logger]
.setLevel(ch.qos.logback.classic.Level.OFF)
- val env = RestTestSetUp.newEnv
+ val env = RestTestSetUp.newEnv
import com.softwaremill.quicklens._
- val myNode = env.mockNodes.node1.modify(_.node.id.value).setTo("my+node").modify(_.hostname).setTo("my+node.rudder.local")
- override def beforeAll(): Unit = {
- val inventory = NodeInventory(
- NodeSummary(
- myNode.id,
- AcceptedInventory,
- "root",
- myNode.hostname,
- myNode.osDetails,
- myNode.policyServerId,
- myNode.keyStatus
- )
- )
- val details = NodeDetails(myNode, inventory, None)
- ZioRuntime.unsafeRun(env.mockNodes.nodeInfoService.nodeBase.updateZIO(nodes => (nodes + (myNode.id -> details)).succeed))
- }
+ val rule = env.mockRules.ruleRepo
+ .get(RuleId(RuleUid("ff44fb97-b65e-43c4-b8c2-0df8d5e8549f")))
+ .runNow
+ .modify(_.id.rev)
+ .setTo(Revision("gitrevision"))
+ val test = new RestTest(env.liftRules)
- override def afterAll(): Unit = {
- ZioRuntime.unsafeRun(env.mockNodes.nodeInfoService.nodeBase.updateZIO(nodes => (nodes - (myNode.id)).succeed))
+ override def beforeAll(): Unit = {
+ ZioRuntime.unsafeRun(env.mockRules.ruleRepo.rulesMap.update(_ + (rule.id -> rule)))
}
- val test = new RestTest(env.liftRules)
+ override def afterAll(): Unit = {}
sequential
@@ -94,16 +82,27 @@ class TestRestPlusInPath extends Specification with BeforeAfterAll {
"A plus in the path part of the url should be kept as a plus" >> {
val mockReq = new MockHttpServletRequest("http://localhost:8080")
mockReq.method = "GET"
- mockReq.path = "/api/latest/nodes/my+node" // should be kept
- mockReq.queryString = "include=minimal"
+ mockReq.path = "/api/latest/rules/ff44fb97-b65e-43c4-b8c2-0df8d5e8549f+gitrevision" // should be kept
mockReq.body = ""
mockReq.headers = Map()
mockReq.contentType = "text/plain"
- // authorize space in response formating
+ // authorize space in response formatting
val expected = {
- """{"action":"nodeDetails","id":"my+node","result":"success","data":""" +
- """{"nodes":[{"id":"my+node","hostname":"my+node.rudder.local","status":"accepted"}]}}"""
+ """{"action":"ruleDetails","id":"ff44fb97-b65e-43c4-b8c2-0df8d5e8549f+gitrevision",
+ |"result":"success","data":{"rules":[{
+ |"id":"ff44fb97-b65e-43c4-b8c2-0df8d5e8549f+gitrevision",
+ |"displayName":"60-rule-technique-std-lib",
+ |"categoryId":"rootRuleCategory",
+ |"shortDescription":"default rule",
+ |"longDescription":"",
+ |"directives":["16617aa8-1f02-4e4a-87b6-d0bcdfb4019f","99f4ef91-537b-4e03-97bc-e65b447514cc",
+ |"e9a1a909-2490-4fc9-95c3-9d0aa01717c9"],
+ |"targets":["special:all"],
+ |"enabled":true,"system":false,"tags":[],"policyMode":"enforce",
+ |"status":{"value":"Partially applied",
+ |"details":"Directive 'directive 16617aa8-1f02-4e4a-87b6-d0bcdfb4019f' disabled, Directive 'directive e9a1a909-2490-4fc9-95c3-9d0aa01717c9' disabled"
+ |}}]}}""".stripMargin.replaceAll("\n", "")
}
test.execRequestResponse(mockReq)(response => {
diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/RudderConfig.scala b/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/RudderConfig.scala
index e7abef21484..33ba7cb9576 100644
--- a/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/RudderConfig.scala
+++ b/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/RudderConfig.scala
@@ -66,30 +66,15 @@ import com.normation.errors.SystemError
import com.normation.inventory.domain._
import com.normation.inventory.ldap.core._
import com.normation.inventory.ldap.provisioning.AddIpValues
-import com.normation.inventory.ldap.provisioning.CheckMachineName
import com.normation.inventory.ldap.provisioning.CheckOsType
-import com.normation.inventory.ldap.provisioning.DefaultInventorySaver
-import com.normation.inventory.ldap.provisioning.DefaultLDIFInventoryLogger
-import com.normation.inventory.ldap.provisioning.FromMotherBoardUuidIdFinder
import com.normation.inventory.ldap.provisioning.LastInventoryDate
-import com.normation.inventory.ldap.provisioning.LogInventoryPreCommit
import com.normation.inventory.ldap.provisioning.NameAndVersionIdFinder
-import com.normation.inventory.ldap.provisioning.PendingNodeIfNodeWasRemoved
-import com.normation.inventory.ldap.provisioning.PostCommitLogger
-import com.normation.inventory.ldap.provisioning.UseExistingMachineIdFinder
-import com.normation.inventory.ldap.provisioning.UseExistingNodeIdFinder
-import com.normation.inventory.ldap.provisioning.UuidMergerPreCommit
import com.normation.inventory.provisioning.fusion.FusionInventoryParser
import com.normation.inventory.provisioning.fusion.PreInventoryParserCheckConsistency
import com.normation.inventory.services.core._
import com.normation.inventory.services.provisioning.DefaultInventoryParser
import com.normation.inventory.services.provisioning.InventoryDigestServiceV1
import com.normation.inventory.services.provisioning.InventoryParser
-import com.normation.inventory.services.provisioning.MachineDNFinderService
-import com.normation.inventory.services.provisioning.NamedMachineDNFinderAction
-import com.normation.inventory.services.provisioning.NamedNodeInventoryDNFinderAction
-import com.normation.inventory.services.provisioning.NodeInventoryDNFinderService
-import com.normation.inventory.services.provisioning.PreCommit
import com.normation.ldap.sdk._
import com.normation.plugins.FilePluginSettingsService
import com.normation.plugins.ReadPluginPackageInfo
@@ -115,14 +100,28 @@ import com.normation.rudder.domain._
import com.normation.rudder.domain.logger.ApplicationLogger
import com.normation.rudder.domain.logger.NodeConfigurationLoggerImpl
import com.normation.rudder.domain.logger.ScheduledJobLoggerPure
+import com.normation.rudder.domain.nodes.NodeGroupId
import com.normation.rudder.domain.queries._
-import com.normation.rudder.facts.nodes.GitNodeFactRepositoryImpl
+import com.normation.rudder.facts.nodes.AppLogNodeFactChangeEventCallback
+import com.normation.rudder.facts.nodes.CacheInvalidateNodeFactEventCallback
+import com.normation.rudder.facts.nodes.CoreNodeFactRepository
+import com.normation.rudder.facts.nodes.EventLogsNodeFactChangeEventCallback
+import com.normation.rudder.facts.nodes.GenerationOnChange
+import com.normation.rudder.facts.nodes.GitNodeFactStorageImpl
+import com.normation.rudder.facts.nodes.HistorizeNodeState
+import com.normation.rudder.facts.nodes.LdapNodeFactStorage
+import com.normation.rudder.facts.nodes.NodeFactChangeEventCallback
+import com.normation.rudder.facts.nodes.NodeFactFullInventoryRepositoryProxy
+import com.normation.rudder.facts.nodes.NodeFactInventorySaver
+import com.normation.rudder.facts.nodes.NodeFactRepository
+import com.normation.rudder.facts.nodes.NodeInfoServiceProxy
import com.normation.rudder.facts.nodes.NoopFactStorage
+import com.normation.rudder.facts.nodes.SoftDaoGetNodesbySofwareName
+import com.normation.rudder.facts.nodes.WoFactNodeRepositoryProxy
import com.normation.rudder.git.GitRepositoryProvider
import com.normation.rudder.git.GitRepositoryProviderImpl
import com.normation.rudder.git.GitRevisionProvider
import com.normation.rudder.inventory.DefaultProcessInventoryService
-import com.normation.rudder.inventory.FactRepositoryPostCommit
import com.normation.rudder.inventory.InventoryFailedHook
import com.normation.rudder.inventory.InventoryFileWatcher
import com.normation.rudder.inventory.InventoryMover
@@ -206,7 +205,6 @@ import com.typesafe.config.ConfigException
import com.typesafe.config.ConfigFactory
import com.unboundid.ldap.sdk.DN
import com.unboundid.ldap.sdk.RDN
-import com.unboundid.ldif.LDIFChangeRecord
import java.io.File
import java.nio.file.attribute.PosixFilePermission
import java.security.Security
@@ -403,7 +401,7 @@ object RudderParsedProperties {
// Here, we define static nouns for all theses properties
//
- private[this] val filteredPasswords = scala.collection.mutable.Buffer[String]()
+ val filteredPasswords = scala.collection.mutable.Buffer[String]()
def logRudderParsedProperties() = {
import scala.jdk.CollectionConverters._
@@ -1094,6 +1092,7 @@ object RudderConfig extends Loggable {
val acceptedNodeQueryProcessor: QueryProcessor = rci.acceptedNodeQueryProcessor
val acceptedNodesDit: InventoryDit = rci.acceptedNodesDit
val agentRegister: AgentRegister = rci.agentRegister
+ val aggregateReportScheduler: FindNewReportsExecution = rci.aggregateReportScheduler
val apiAuthorizationLevelService: DefaultApiAuthorizationLevel = rci.apiAuthorizationLevelService
val apiDispatcher: RudderEndpointDispatcher = rci.apiDispatcher
val asyncComplianceService: AsyncComplianceService = rci.asyncComplianceService
@@ -1106,9 +1105,9 @@ object RudderConfig extends Loggable {
val campaignEventRepo: CampaignEventRepositoryImpl = rci.campaignEventRepo
val campaignSerializer: CampaignSerializer = rci.campaignSerializer
val categoryHierarchyDisplayer: CategoryHierarchyDisplayer = rci.categoryHierarchyDisplayer
+ val changeRequestChangesSerialisation: ChangeRequestChangesSerialisation = rci.changeRequestChangesSerialisation
val changeRequestChangesUnserialisation: ChangeRequestChangesUnserialisation = rci.changeRequestChangesUnserialisation
val changeRequestEventLogService: ChangeRequestEventLogService = rci.changeRequestEventLogService
- val checkInventoryUpdate: CheckInventoryUpdate = rci.checkInventoryUpdate
val checkTechniqueLibrary: CheckTechniqueLibrary = rci.checkTechniqueLibrary
val clearCacheService: ClearCacheService = rci.clearCacheService
val cmdbQueryParser: CmdbQueryParser = rci.cmdbQueryParser
@@ -1131,7 +1130,7 @@ object RudderConfig extends Loggable {
val eventLogDetailsService: EventLogDetailsService = rci.eventLogDetailsService
val eventLogRepository: EventLogRepository = rci.eventLogRepository
val findExpectedReportRepository: FindExpectedReportRepository = rci.findExpectedReportRepository
- val fullInventoryRepository: LDAPFullInventoryRepository = rci.fullInventoryRepository
+ val fullInventoryRepository: FullInventoryRepository[Unit] = rci.fullInventoryRepository
val gitRevisionProvider: GitRevisionProvider = rci.gitRevisionProvider
val healthcheckNotificationService: HealthcheckNotificationService = rci.healthcheckNotificationService
val historizeNodeCountBatch: IOResult[Unit] = rci.historizeNodeCountBatch
@@ -1148,11 +1147,10 @@ object RudderConfig extends Loggable {
val mainCampaignService: MainCampaignService = rci.mainCampaignService
val ncfTechniqueReader: ncf.EditorTechniqueReader = rci.ncfTechniqueReader
val newNodeManager: NewNodeManager = rci.newNodeManager
- val newNodeManagerHooks: NewNodeManagerHooks = rci.newNodeManagerHooks
val nodeDit: NodeDit = rci.nodeDit
+ val nodeFactRepository: NodeFactRepository = rci.nodeFactRepository
val nodeGrid: NodeGrid = rci.nodeGrid
val nodeInfoService: NodeInfoService = rci.nodeInfoService
- val nodeSummaryService: NodeSummaryService = rci.nodeSummaryService
val pendingNodeCheckGroup: CheckPendingNodeInDynGroups = rci.pendingNodeCheckGroup
val pendingNodesDit: InventoryDit = rci.pendingNodesDit
val personIdentService: PersonIdentService = rci.personIdentService
@@ -1187,6 +1185,7 @@ object RudderConfig extends Loggable {
val ruleApplicationStatus: RuleApplicationStatusService = rci.ruleApplicationStatus
val ruleCategoryService: RuleCategoryService = rci.ruleCategoryService
val rwLdap: LDAPConnectionProvider[RwLDAPConnection] = rci.rwLdap
+ val secretEventLogService: SecretEventLogService = rci.secretEventLogService
val sharedFileApi: SharedFilesAPI = rci.sharedFileApi
val snippetExtensionRegister: SnippetExtensionRegister = rci.snippetExtensionRegister
val srvGrid: SrvGrid = rci.srvGrid
@@ -1207,9 +1206,6 @@ object RudderConfig extends Loggable {
val woRuleRepository: WoRuleRepository = rci.woRuleRepository
val workflowEventLogService: WorkflowEventLogService = rci.workflowEventLogService
val workflowLevelService: DefaultWorkflowLevel = rci.workflowLevelService
- val aggregateReportScheduler: FindNewReportsExecution = rci.aggregateReportScheduler
- val secretEventLogService: SecretEventLogService = rci.secretEventLogService
- val changeRequestChangesSerialisation: ChangeRequestChangesSerialisation = rci.changeRequestChangesSerialisation
val gitRepo: GitRepositoryProvider = rci.gitRepo
val gitModificationRepository: GitModificationRepository = rci.gitModificationRepository
@@ -1274,9 +1270,7 @@ case class RudderServiceApi(
ruleApplicationStatus: RuleApplicationStatusService,
propertyEngineService: PropertyEngineService,
newNodeManager: NewNodeManager,
- newNodeManagerHooks: NewNodeManagerHooks,
nodeGrid: NodeGrid,
- nodeSummaryService: NodeSummaryService,
jsTreeUtilService: JsTreeUtilService,
directiveEditorService: DirectiveEditorService,
userPropertyService: UserPropertyService,
@@ -1285,7 +1279,6 @@ case class RudderServiceApi(
policyServerManagementService: PolicyServerManagementService,
updateDynamicGroupsService: DynGroupUpdaterService,
updateDynamicGroups: UpdateDynamicGroups,
- checkInventoryUpdate: CheckInventoryUpdate,
purgeDeletedInventories: PurgeDeletedInventories,
purgeUnreferencedSoftwares: PurgeUnreferencedSoftwares,
databaseManager: DatabaseManager,
@@ -1300,7 +1293,7 @@ case class RudderServiceApi(
personIdentService: PersonIdentService,
gitRevisionProvider: GitRevisionProvider,
logDisplayer: LogDisplayer,
- fullInventoryRepository: LDAPFullInventoryRepository,
+ fullInventoryRepository: FullInventoryRepository[Unit],
acceptedNodeQueryProcessor: QueryProcessor,
categoryHierarchyDisplayer: CategoryHierarchyDisplayer,
dynGroupService: DynGroupService,
@@ -1370,7 +1363,8 @@ case class RudderServiceApi(
secretEventLogService: SecretEventLogService,
changeRequestChangesSerialisation: ChangeRequestChangesSerialisation,
gitRepo: GitRepositoryProvider,
- gitModificationRepository: GitModificationRepository
+ gitModificationRepository: GitModificationRepository,
+ nodeFactRepository: NodeFactRepository
)
/*
@@ -1393,8 +1387,7 @@ object RudderConfigInit {
lazy val clearableCache: Seq[CachedRepository] = Seq(
cachedAgentRunRepository,
recentChangesService,
- reportingServiceImpl,
- nodeInfoServiceImpl
+ reportingServiceImpl
)
lazy val pluginSettingsService = new FilePluginSettingsService(
@@ -1543,7 +1536,7 @@ object RudderConfigInit {
lazy val yamlTechniqueSerializer = new YamlTechniqueSerializer(resourceFileService)
- lazy val linkUtil = new LinkUtil(roRuleRepository, roNodeGroupRepository, roDirectiveRepository, nodeInfoServiceImpl)
+ lazy val linkUtil = new LinkUtil(roRuleRepository, roNodeGroupRepository, roDirectiveRepository, nodeFactInfoService)
// REST API
lazy val restApiAccounts = new RestApiAccounts(
roApiAccountRepository,
@@ -1563,7 +1556,7 @@ object RudderConfigInit {
acceptedNodesDit,
rudderDit,
roDirectiveRepository,
- nodeInfoService
+ nodeFactRepository
),
userService,
linkUtil
@@ -1602,7 +1595,7 @@ object RudderConfigInit {
woRuleCategoryRepository,
roDirectiveRepository,
roNodeGroupRepository,
- nodeInfoService,
+ nodeFactInfoService,
configService.rudder_global_policy_mode _,
ruleApplicationStatus
)
@@ -1690,73 +1683,31 @@ object RudderConfigInit {
)
}
- lazy val nodeApiService2 = new NodeApiService2(
- newNodeManager,
- nodeInfoService,
- removeNodeService,
- uuidGen,
- restExtractorService,
- restDataSerializer
- )
-
- lazy val nodeApiService4 = new NodeApiService4(
- fullInventoryRepository,
- nodeInfoService,
- softwareInventoryDAO,
- uuidGen,
- restExtractorService,
- restDataSerializer,
- roAgentRunsRepository
- )
-
- lazy val nodeApiService8 = {
- new NodeApiService8(
- woNodeRepository,
- nodeInfoService,
- uuidGen,
- asyncDeploymentAgent,
- RUDDER_RELAY_API,
- userService
- )
- }
-
- lazy val nodeApiService12 = new NodeApiService12(
- removeNodeService,
- uuidGen,
- restDataSerializer
- )
-
- lazy val nodeApiService6 = new NodeApiService6(
- nodeInfoService,
- fullInventoryRepository,
- softwareInventoryDAO,
- restExtractorService,
- restDataSerializer,
- queryProcessor,
- inventoryQueryChecker,
- roAgentRunsRepository
- )
-
- lazy val nodeApiService13 = new NodeApiService13(
- nodeInfoService,
- cachedAgentRunRepository,
- readOnlySoftwareDAO,
- restExtractorService,
- () => configService.rudder_global_policy_mode().toBox,
- reportingServiceImpl,
- roNodeGroupRepository,
- roLDAPParameterRepository
- )
-
- lazy val nodeApiService16 = new NodeApiService15(
- fullInventoryRepository,
+ lazy val nodeApiService = new NodeApiService(
rwLdap,
+ nodeFactRepository,
+ factFullInventoryRepo,
+ roNodeGroupRepository,
+ roLDAPParameterRepository,
+ roAgentRunsRepository,
+ woFactNodeRepository,
ldapEntityMapper,
- newNodeManager,
stringUuidGenerator,
nodeDit,
pendingNodesDit,
- acceptedNodesDit
+ acceptedNodesDit,
+ nodeFactInfoService,
+ newNodeManagerImpl,
+ removeNodeServiceImpl,
+ restExtractorService,
+ restDataSerializer,
+ reportingServiceImpl,
+ queryProcessor,
+ inventoryQueryChecker,
+ asyncDeploymentAgent,
+ userService,
+ () => configService.rudder_global_policy_mode().toBox,
+ RUDDER_RELAY_API
)
lazy val parameterApiService2 = {
@@ -1806,14 +1757,14 @@ object RudderConfigInit {
healthcheckService,
healthcheckNotificationService,
restDataSerializer,
- softwareService
+ deprecated.softwareService
)
- lazy val ruleInternalApiService = new RuleInternalApiService(roRuleRepository, roNodeGroupRepository, nodeInfoService)
+ lazy val ruleInternalApiService = new RuleInternalApiService(roRuleRepository, roNodeGroupRepository, nodeFactInfoService)
lazy val complianceAPIService = new ComplianceAPIService(
roRuleRepository,
- nodeInfoService,
+ nodeFactInfoService,
roNodeGroupRepository,
reportingService,
roDirectiveRepository,
@@ -1877,79 +1828,73 @@ object RudderConfigInit {
)
}
- lazy val automaticMerger: PreCommit = new UuidMergerPreCommit(
- uuidGen,
- acceptedNodesDit,
- new NodeInventoryDNFinderService(
- Seq(
- // start by trying to use an already given UUID
- NamedNodeInventoryDNFinderAction(
- "use_existing_id",
- new UseExistingNodeIdFinder(inventoryDitService, roLdap, acceptedNodesDit.BASE_DN.getParent)
- )
- )
- ),
- new MachineDNFinderService(
- Seq(
- // start by trying to use an already given UUID
- NamedMachineDNFinderAction(
- "use_existing_id",
- new UseExistingMachineIdFinder(inventoryDitService, roLdap, acceptedNodesDit.BASE_DN.getParent)
- ), // look if it's in the accepted inventories
-
- NamedMachineDNFinderAction(
- "check_mother_board_uuid_accepted",
- new FromMotherBoardUuidIdFinder(roLdap, acceptedNodesDit, inventoryDitService)
- ), // see if it's in the "pending" branch
-
- NamedMachineDNFinderAction(
- "check_mother_board_uuid_pending",
- new FromMotherBoardUuidIdFinder(roLdap, pendingNodesDit, inventoryDitService)
- ), // see if it's in the "removed" branch
-
- NamedMachineDNFinderAction(
- "check_mother_board_uuid_removed",
- new FromMotherBoardUuidIdFinder(roLdap, removedNodesDitImpl, inventoryDitService)
- )
- )
- ),
- new NameAndVersionIdFinder(
- "check_name_and_version",
- roLdap,
- inventoryMapper,
- acceptedNodesDit
- )
- )
-
- lazy val gitFactRepo = GitRepositoryProviderImpl
+ lazy val gitFactRepoProvider = GitRepositoryProviderImpl
.make(RUDDER_GIT_ROOT_FACT_REPO)
.runOrDie(err => new RuntimeException(s"Error when initializing git configuration repository: " + err.fullMsg))
- lazy val gitFactRepoGC = new GitGC(gitFactRepo, RUDDER_GIT_GC)
- lazy val nodeFactStorage = if (RUDDER_GIT_FACT_WRITE_NODES) {
- val r = new GitNodeFactRepositoryImpl(gitFactRepo, RUDDER_GROUP_OWNER_CONFIG_REPO, RUDDER_GIT_FACT_COMMIT_NODES)
+ lazy val gitFactRepoGC = new GitGC(gitFactRepoProvider, RUDDER_GIT_GC)
+ gitFactRepoGC.start()
+ lazy val gitFactStorage = if (RUDDER_GIT_FACT_WRITE_NODES) {
+ val r = new GitNodeFactStorageImpl(gitFactRepoProvider, Some(RUDDER_GROUP_OWNER_CONFIG_REPO), RUDDER_GIT_FACT_COMMIT_NODES)
r.checkInit().runOrDie(err => new RuntimeException(s"Error when checking fact repository init: " + err.fullMsg))
r
} else NoopFactStorage
- lazy val ldifInventoryLogger = new DefaultLDIFInventoryLogger(LDIF_TRACELOG_ROOT_DIR)
- lazy val inventorySaver = new DefaultInventorySaver(
+ // TODO WARNING POC: this can't work on a machine with lots of node
+ lazy val ldapNodeFactStorage = new LdapNodeFactStorage(
rwLdap,
- acceptedNodesDit,
+ nodeDit,
+ inventoryDitService,
+ ldapEntityMapper,
inventoryMapper,
+ nodeReadWriteMutex,
+ deprecated.ldapFullInventoryRepository,
+ deprecated.softwareInventoryDAO,
+ deprecated.ldapSoftwareSave,
+ uuidGen
+ )
+
+ lazy val getNodeBySoftwareName = new SoftDaoGetNodesbySofwareName(deprecated.softwareInventoryDAO)
+
+ lazy val nodeFactRepository = {
+
+ val callbacks = Chunk[NodeFactChangeEventCallback](
+ new AppLogNodeFactChangeEventCallback(),
+ new EventLogsNodeFactChangeEventCallback(eventLogRepository),
+ new HistorizeNodeState(
+ inventoryHistoryJdbcRepository,
+ ldapNodeFactStorage,
+ gitFactStorage,
+ (KEEP_DELETED_NODE_FACT_DURATION.getSeconds == 0)
+ )
+ )
+
+ val repo = CoreNodeFactRepository.make(ldapNodeFactStorage, getNodeBySoftwareName, callbacks).runNow
+ repo
+ }
+
+ lazy val inventorySaver = new NodeFactInventorySaver(
+ nodeFactRepository,
(
CheckOsType
- :: automaticMerger
- :: CheckMachineName
+ // removed: noe and machine go together, and we have UUIDs for nodes
+ // :: automaticMerger
+ // :: CheckMachineName
:: new LastInventoryDate()
:: AddIpValues
- :: new LogInventoryPreCommit(inventoryMapper, ldifInventoryLogger)
+ // removed: we only store the files, not LDIF of changes
+ // :: new LogInventoryPreCommit(inventoryMapper, ldifInventoryLogger)
:: Nil
),
(
- new PendingNodeIfNodeWasRemoved(fullInventoryRepository)
- :: new FactRepositoryPostCommit[Seq[LDIFChangeRecord]](nodeFactStorage, nodeInfoService)
- :: new PostCommitLogger(ldifInventoryLogger)
- :: new PostCommitInventoryHooks[Seq[LDIFChangeRecord]](HOOKS_D, HOOKS_IGNORE_SUFFIXES)
+ // removed: nodes are fully deleted now
+// new PendingNodeIfNodeWasRemoved(fullInventoryRepository)
+ // already commited in fact repos
+// new FactRepositoryPostCommit[Unit](factRepo, nodeFactInfoService)
+ // deprecated: we use fact repo now
+// :: new PostCommitLogger(ldifInventoryLogger)
+ new PostCommitInventoryHooks[Unit](HOOKS_D, HOOKS_IGNORE_SUFFIXES)
+ // removed: this is done as a callback of CoreNodeFactRepos
+ // :: new TriggerPolicyGenerationPostCommit[Unit](asyncDeploymentAgent, uuidGen)
:: Nil
)
)
@@ -1987,7 +1932,7 @@ object RudderConfigInit {
pipelinedInventoryParser,
inventorySaver,
maxParallel,
- new InventoryDigestServiceV1(fullInventoryRepository.get),
+ new InventoryDigestServiceV1((id: NodeId) => factFullInventoryRepo.get(id)),
checkLdapAlive
)
}
@@ -2027,6 +1972,8 @@ object RudderConfigInit {
)
}
+ lazy val factFullInventoryRepo = new NodeFactFullInventoryRepositoryProxy(nodeFactRepository)
+
lazy val archiveApi = {
val archiveBuilderService =
new ZipArchiveBuilderService(new FileArchiveNameService(), configurationRepository, gitParseTechniqueLibrary)
@@ -2081,7 +2028,7 @@ object RudderConfigInit {
import com.normation.rudder.rest.lift._
val nodeInheritedProperties =
- new NodeApiInheritedProperties(nodeInfoService, roNodeGroupRepository, roLDAPParameterRepository)
+ new NodeApiInheritedProperties(nodeFactInfoService, roNodeGroupRepository, roLDAPParameterRepository)
val groupInheritedProperties = new GroupApiInheritedProperties(roNodeGroupRepository, roLDAPParameterRepository)
val campaignApi = new lift.CampaignApi(
@@ -2115,13 +2062,7 @@ object RudderConfigInit {
new NodeApi(
restExtractorService,
restDataSerializer,
- nodeApiService2,
- nodeApiService4,
- nodeApiService6,
- nodeApiService8,
- nodeApiService12,
- nodeApiService13,
- nodeApiService16,
+ nodeApiService,
nodeInheritedProperties,
DeleteMode.Erase // only supported mode for Rudder 8.0
),
@@ -2132,7 +2073,7 @@ object RudderConfigInit {
asyncDeploymentAgent,
stringUuidGenerator,
policyServerManagementService,
- nodeInfoService
+ nodeFactInfoService
),
new TechniqueApi(
restExtractorService,
@@ -2348,15 +2289,15 @@ object RudderConfigInit {
* For now, we don't want to query server other
* than the accepted ones.
*/
- lazy val getSubGroupChoices = () => roLdapNodeGroupRepository.getAll().map(seq => seq.map(g => SubGroupChoice(g.id, g.name)))
- lazy val ditQueryDataImpl = new DitQueryData(acceptedNodesDitImpl, nodeDit, rudderDit, getSubGroupChoices)
+ lazy val getSubGroupChoices = new DefaultSubGroupComparatorRepository(roLdapNodeGroupRepository)
+ lazy val nodeQueryData = new NodeQueryCriteriaData(() => getSubGroupChoices)
+ lazy val ditQueryDataImpl = new DitQueryData(acceptedNodesDitImpl, nodeDit, rudderDit, nodeQueryData)
lazy val queryParser = new CmdbQueryParser with DefaultStringQueryParser with JsonQueryLexer {
override val criterionObjects = Map[String, ObjectCriterion]() ++ ditQueryDataImpl.criteriaMap
}
lazy val inventoryMapper: InventoryMapper =
new InventoryMapper(inventoryDitService, pendingNodesDitImpl, acceptedNodesDitImpl, removedNodesDitImpl)
- lazy val fullInventoryFromLdapEntries: FullInventoryFromLdapEntries =
- new FullInventoryFromLdapEntriesImpl(inventoryDitService, inventoryMapper)
+
lazy val ldapDiffMapper = new LDAPDiffMapper(ldapEntityMapper, queryParser)
lazy val activeTechniqueCategoryUnserialisation = new ActiveTechniqueCategoryUnserialisationImpl
@@ -2517,73 +2458,49 @@ object RudderConfigInit {
}
// query processor for accepted nodes
- lazy val queryProcessor = new AcceptedNodesLDAPQueryProcessor(
- nodeDitImpl,
- acceptedNodesDitImpl,
- new InternalLDAPQueryProcessor(roLdap, acceptedNodesDitImpl, nodeDit, ditQueryDataImpl, ldapEntityMapper),
- nodeInfoServiceImpl
+ lazy val queryProcessor = new NodeFactQueryProcessor(
+ nodeFactRepository,
+ new DefaultSubGroupComparatorRepository(roNodeGroupRepository),
+ deprecated.internalAcceptedQueryProcessor,
+ AcceptedInventory
)
// we need a roLdap query checker for nodes in pending
- lazy val inventoryQueryChecker = new PendingNodesLDAPQueryChecker(
- new InternalLDAPQueryProcessor(
- roLdap,
- pendingNodesDitImpl,
- nodeDit, // here, we don't want to look for subgroups to show them in the form => always return an empty list
-
- new DitQueryData(pendingNodesDitImpl, nodeDit, rudderDit, () => Nil.succeed),
- ldapEntityMapper
- ),
- nodeInfoServiceImpl
+ lazy val inventoryQueryChecker = new NodeFactQueryProcessor(
+ nodeFactRepository,
+ new DefaultSubGroupComparatorRepository(roNodeGroupRepository),
+ deprecated.internalPendingQueryProcessor,
+ PendingInventory
)
- lazy val dynGroupServiceImpl = new DynGroupServiceImpl(rudderDitImpl, roLdap, ldapEntityMapper)
+
+ lazy val dynGroupServiceImpl = new DynGroupServiceImpl(rudderDitImpl, roLdap, ldapEntityMapper)
lazy val pendingNodeCheckGroup = new CheckPendingNodeInDynGroups(inventoryQueryChecker)
- lazy val ldapFullInventoryRepository =
- new FullInventoryRepositoryImpl(inventoryDitService, inventoryMapper, rwLdap)
- lazy val fullInventoryRepository = ldapFullInventoryRepository
- lazy val unitRefuseGroup: UnitRefuseInventory =
+ lazy val unitRefuseGroup: UnitRefuseInventory =
new RefuseGroups("refuse_node:delete_id_in_groups", roLdapNodeGroupRepository, woLdapNodeGroupRepository)
- lazy val acceptInventory: UnitAcceptInventory with UnitRefuseInventory =
- new AcceptInventory("accept_new_server:inventory", pendingNodesDitImpl, acceptedNodesDitImpl, ldapFullInventoryRepository)
- lazy val acceptNodeAndMachineInNodeOu: UnitAcceptInventory with UnitRefuseInventory = {
- new AcceptFullInventoryInNodeOu(
- "accept_new_server:ou=node",
- nodeDitImpl,
- rwLdap,
- ldapEntityMapper,
- PendingInventory,
- () => configService.rudder_node_onaccept_default_policy_mode().toBox,
- () => configService.rudder_node_onaccept_default_state().toBox
- )
- }
- lazy val acceptHostnameAndIp: UnitAcceptInventory = new AcceptHostnameAndIp(
+ lazy val acceptHostnameAndIp: UnitCheckAcceptInventory = new AcceptHostnameAndIp(
"accept_new_server:check_hostname_unicity",
- AcceptedInventory,
queryProcessor,
ditQueryDataImpl,
psMngtService,
- nodeInfoServiceImpl,
+ nodeFactRepository,
configService.node_accept_duplicated_hostname()
)
- lazy val historizeNodeStateOnChoice: UnitAcceptInventory with UnitRefuseInventory = {
- new HistorizeNodeStateOnChoice(
- "accept_or_refuse_new_node:historize_inventory",
- ldapFullInventoryRepository,
- inventoryHistoryJdbcRepository,
- PendingInventory
+ // used in accept node to see & store inventories on acceptation
+ lazy val inventoryHistoryLogRepository: InventoryHistoryLogRepository = {
+ val fullInventoryFromLdapEntries: FullInventoryFromLdapEntries =
+ new FullInventoryFromLdapEntriesImpl(inventoryDitService, inventoryMapper)
+
+ new InventoryHistoryLogRepository(
+ HISTORY_INVENTORIES_ROOTDIR,
+ new FullInventoryFileParser(fullInventoryFromLdapEntries, inventoryMapper)
)
}
- lazy val updateFactRepoOnChoice: UnitAcceptInventory with UnitRefuseInventory = new UpdateFactRepoOnChoice(
- "accept_or_refuse_new_node:update_fact_repo",
- PendingInventory,
- nodeFactStorage
- )
- lazy val nodeGridImpl = new NodeGrid(ldapFullInventoryRepository, nodeInfoServiceImpl, configService)
+ lazy val nodeGridImpl = new NodeGrid(factFullInventoryRepo, nodeFactInfoService, configService)
lazy val modificationService =
new ModificationService(logRepository, gitModificationRepository, itemArchiveManagerImpl, uuidGen)
@@ -2593,29 +2510,17 @@ object RudderConfigInit {
logRepository,
roLdapNodeGroupRepository,
roLdapDirectiveRepository,
- nodeInfoServiceImpl,
+ nodeFactInfoService,
roLDAPRuleCategoryRepository,
modificationService,
personIdentServiceImpl,
linkUtil,
diffDisplayer
)
- lazy val databaseManagerImpl = new DatabaseManagerImpl(reportsRepositoryImpl, updateExpectedRepo)
- lazy val softwareInventoryDAO: ReadOnlySoftwareDAO =
- new ReadOnlySoftwareDAOImpl(inventoryDitService, roLdap, inventoryMapper)
- lazy val readOnlySoftwareDAO = softwareInventoryDAO
- lazy val softwareInventoryRWDAO: WriteOnlySoftwareDAO = new WriteOnlySoftwareDAOImpl(acceptedNodesDitImpl, rwLdap)
- lazy val softwareService: SoftwareService =
- new SoftwareServiceImpl(softwareInventoryDAO, softwareInventoryRWDAO, acceptedNodesDit)
- lazy val nodeSummaryServiceImpl = new NodeSummaryServiceImpl(inventoryDitService, inventoryMapper, roLdap)
+ lazy val databaseManagerImpl = new DatabaseManagerImpl(reportsRepositoryImpl, updateExpectedRepo)
+
lazy val inventoryHistoryJdbcRepository = new InventoryHistoryJdbcRepository(doobie)
- lazy val inventoryHistoryLogRepository: InventoryHistoryLogRepository = {
- new InventoryHistoryLogRepository(
- HISTORY_INVENTORIES_ROOTDIR,
- new FullInventoryFileParser(fullInventoryFromLdapEntries, inventoryMapper)
- )
- }
lazy val personIdentServiceImpl: PersonIdentService = new TrivialPersonIdentService
lazy val personIdentService = personIdentServiceImpl
@@ -2757,17 +2662,7 @@ object RudderConfigInit {
)
lazy val woRuleRepository = woLdapRuleRepository
- lazy val woLdapNodeRepository: WoNodeRepository = new WoLDAPNodeRepository(
- nodeDitImpl,
- acceptedNodesDit,
- ldapEntityMapper,
- rwLdap,
- logRepository,
- nodeReadWriteMutex,
- cachedNodeConfigurationService,
- reportingServiceImpl
- )
- lazy val woNodeRepository = woLdapNodeRepository
+ lazy val woFactNodeRepository: WoNodeRepository = new WoFactNodeRepositoryProxy(nodeFactRepository)
lazy val roLdapNodeGroupRepository = new RoLDAPNodeGroupRepository(
rudderDitImpl,
@@ -2865,7 +2760,7 @@ object RudderConfigInit {
}
lazy val globalAgentRunService: AgentRunIntervalService = {
new AgentRunIntervalServiceImpl(
- nodeInfoServiceImpl,
+ nodeFactInfoService,
() => configService.agent_run_interval().toBox,
() => configService.agent_run_start_hour().toBox,
() => configService.agent_run_start_minute().toBox,
@@ -2955,7 +2850,7 @@ object RudderConfigInit {
ruleValService,
systemVariableService,
nodeConfigurationHashRepo,
- nodeInfoServiceImpl,
+ nodeFactInfoService,
updateExpectedRepo,
roNodeGroupRepository,
roDirectiveRepository,
@@ -2963,7 +2858,7 @@ object RudderConfigInit {
ruleApplicationStatusImpl,
roParameterServiceImpl,
interpolationCompiler,
- ldapFullInventoryRepository,
+ factFullInventoryRepo,
globalComplianceModeService,
globalAgentRunService,
reportingServiceImpl,
@@ -3004,56 +2899,36 @@ object RudderConfigInit {
}
lazy val asyncDeploymentAgent = asyncDeploymentAgentImpl
- lazy val newNodeHookRunner = new NewNodeManagerHooksImpl(
- nodeInfoServiceImpl,
- HOOKS_D,
- HOOKS_IGNORE_SUFFIXES
- )
-
lazy val newNodeManagerImpl = {
+
// the sequence of unit process to accept a new inventory
val unitAcceptors = {
- historizeNodeStateOnChoice ::
- updateFactRepoOnChoice ::
- acceptNodeAndMachineInNodeOu ::
- acceptInventory ::
acceptHostnameAndIp ::
Nil
}
// the sequence of unit process to refuse a new inventory
val unitRefusors = {
- historizeNodeStateOnChoice ::
- updateFactRepoOnChoice ::
unitRefuseGroup ::
- acceptInventory ::
Nil
}
+ val hooksRunner = new NewNodeManagerHooksImpl(nodeFactRepository, HOOKS_D, HOOKS_IGNORE_SUFFIXES)
- val composed = new ComposedNewNodeManager[Seq[LDIFChangeRecord]](
- ldapFullInventoryRepository,
- nodeSummaryServiceImpl,
+ val composedManager = new ComposedNewNodeManager[Unit](
+ nodeFactRepository,
unitAcceptors,
unitRefusors,
- inventoryHistoryJdbcRepository,
- eventLogRepository,
- dyngroupUpdaterBatch,
- cachedNodeConfigurationService,
- reportingServiceImpl,
- List(nodeInfoServiceImpl),
- newNodeHookRunner
+ hooksRunner
)
+ val listNodes = new FactListNewNodes(nodeFactRepository)
- val listNodes = new LdapListNewNode(
- roLdap,
- nodeSummaryServiceImpl,
- pendingNodesDitImpl
+ new NewNodeManagerImpl[Unit](
+ composedManager,
+ listNodes
)
-
- new NewNodeManagerImpl(composed, listNodes)
}
- lazy val newNodeManager: NewNodeManager = newNodeManagerImpl
+ /////// reporting ///////
lazy val nodeConfigurationHashRepo: NodeConfigurationHashRepository = {
val x = new FileBasedNodeConfigurationHashRepository(FileBasedNodeConfigurationHashRepository.defaultHashesPath)
@@ -3061,14 +2936,14 @@ object RudderConfigInit {
x
}
- lazy val reportingServiceImpl = {
+ lazy val reportingServiceImpl: CachedReportingServiceImpl = {
val reportingServiceImpl = new CachedReportingServiceImpl(
new ReportingServiceImpl(
findExpectedRepo,
reportsRepositoryImpl,
roAgentRunsRepository,
globalAgentRunService,
- nodeInfoServiceImpl,
+ nodeFactInfoService,
roLdapDirectiveRepository,
roRuleRepository,
cachedNodeConfigurationService,
@@ -3077,7 +2952,7 @@ object RudderConfigInit {
() => configService.rudder_compliance_unexpected_report_interpretation().toBox,
RUDDER_JDBC_BATCH_MAX_SIZE
),
- nodeInfoServiceImpl,
+ nodeFactInfoService,
RUDDER_JDBC_BATCH_MAX_SIZE, // use same size as for SQL requests
complianceRepositoryImpl
@@ -3087,6 +2962,7 @@ object RudderConfigInit {
cachedNodeConfigurationService.addHook(reportingServiceImpl)
reportingServiceImpl
}
+
lazy val reportingService: ReportingService = reportingServiceImpl
lazy val pgIn = new PostgresqlInClause(70)
@@ -3161,17 +3037,8 @@ object RudderConfigInit {
ruleCatReadWriteMutex
)
lazy val eventLogDeploymentServiceImpl = new EventLogDeploymentService(logRepository, eventLogDetailsServiceImpl)
- lazy val nodeInfoServiceImpl = new NodeInfoServiceCachedImpl(
- roLdap,
- nodeDitImpl,
- acceptedNodesDitImpl,
- removedNodesDitImpl,
- pendingNodesDitImpl,
- ldapEntityMapper,
- inventoryMapper,
- FiniteDuration(LDAP_CACHE_NODE_INFO_MIN_INTERVAL.toMillis, "millis")
- )
- lazy val nodeInfoService: NodeInfoService = nodeInfoServiceImpl
+
+ lazy val nodeFactInfoService = new NodeInfoServiceProxy(nodeFactRepository)
lazy val dependencyAndDeletionService: DependencyAndDeletionService = new DependencyAndDeletionServiceImpl(
new FindDependenciesImpl(roLdap, rudderDitImpl, ldapEntityMapper),
roLdapDirectiveRepository,
@@ -3244,53 +3111,35 @@ object RudderConfigInit {
*/
lazy val postNodeDeleteActions = Ref
.make(
- new RemoveNodeInfoFromCache(nodeInfoServiceImpl)
+ // new RemoveNodeInfoFromCache(ldapNodeInfoServiceImpl)
+ new RemoveNodeFromGroups(roNodeGroupRepository, woNodeGroupRepository, uuidGen)
:: new CloseNodeConfiguration(updateExpectedRepo)
- :: new RemoveNodeFromComplianceCache(cachedNodeConfigurationService, reportingServiceImpl)
:: new DeletePolicyServerPolicies(policyServerManagementService)
:: new ResetKeyStatus(rwLdap, removedNodesDitImpl)
:: new CleanUpCFKeys()
:: new CleanUpNodePolicyFiles("/var/rudder/share")
- :: new DeleteNodeFact(nodeFactStorage)
- :: new StoreDeleteEventHistory(inventoryHistoryJdbcRepository, (KEEP_DELETED_NODE_FACT_DURATION.getSeconds == 0))
:: Nil
)
.runNow
- lazy val removeNodeServiceImpl = {
- val backend = new LdapRemoveNodeBackend(
- nodeDitImpl,
- pendingNodesDitImpl,
- acceptedNodesDitImpl,
- removedNodesDitImpl,
- rwLdap,
- ldapFullInventoryRepository,
- nodeReadWriteMutex
- )
+ lazy val factRemoveNodeBackend = new FactRemoveNodeBackend(nodeFactRepository)
- new RemoveNodeServiceImpl(
- backend,
- nodeInfoService,
- pathComputer,
- newNodeManager,
- eventLogRepository,
- postNodeDeleteActions,
- HOOKS_D,
- HOOKS_IGNORE_SUFFIXES
- )
- }
- lazy val removeNodeService: RemoveNodeService = removeNodeServiceImpl
- lazy val purgeDeletedNodes = new PurgeDeletedNodesImpl(
- rwLdap,
- removedNodesDitImpl,
- ldapFullInventoryRepository
+ lazy val removeNodeServiceImpl = new RemoveNodeServiceImpl(
+ // deprecated.ldapRemoveNodeBackend,
+ factRemoveNodeBackend,
+ nodeFactInfoService,
+ pathComputer,
+ newNodeManagerImpl,
+ postNodeDeleteActions,
+ HOOKS_D,
+ HOOKS_IGNORE_SUFFIXES
)
lazy val healthcheckService = new HealthcheckService(
List(
CheckCoreNumber,
CheckFreeSpace,
- new CheckFileDescriptorLimit(nodeInfoService)
+ new CheckFileDescriptorLimit(nodeFactInfoService)
)
)
@@ -3316,7 +3165,7 @@ object RudderConfigInit {
new CheckConnections(dataSourceProvider, rwLdap),
new MigrateEventLogEnforceSchema(doobie),
new MigrateNodeAcceptationInventories(
- nodeInfoService,
+ nodeFactInfoService,
doobie,
inventoryHistoryLogRepository,
inventoryHistoryJdbcRepository,
@@ -3341,6 +3190,13 @@ object RudderConfigInit {
new CheckRudderGlobalParameter(roLDAPParameterRepository, woLDAPParameterRepository, uuidGen),
new CheckInitXmlExport(itemArchiveManagerImpl, personIdentServiceImpl, uuidGen),
+ new MigrateNodeAcceptationInventories(
+ nodeFactInfoService,
+ doobie,
+ inventoryHistoryLogRepository,
+ inventoryHistoryJdbcRepository,
+ KEEP_DELETED_NODE_FACT_DURATION
+ ),
new CheckNcfTechniqueUpdate(
ncfTechniqueWriter,
roLDAPApiAccountRepository.systemAPIAccount,
@@ -3364,7 +3220,7 @@ object RudderConfigInit {
uuidGen
),
new CreateSystemToken(roLDAPApiAccountRepository.systemAPIAccount),
- new LoadNodeComplianceCache(nodeInfoService, reportingServiceImpl)
+ new LoadNodeComplianceCache(nodeFactInfoService, reportingServiceImpl)
)
//////////////////////////////////////////////////////////////////////////////////////////
@@ -3451,7 +3307,7 @@ object RudderConfigInit {
reportsRepositoryImpl,
roLdapRuleRepository,
roLdapDirectiveRepository,
- nodeInfoServiceImpl,
+ nodeFactInfoService,
RUDDER_BATCH_REPORTS_LOGINTERVAL
)
@@ -3462,7 +3318,7 @@ object RudderConfigInit {
lazy val snippetExtensionRegister: SnippetExtensionRegister = new SnippetExtensionRegisterImpl()
lazy val cachedNodeConfigurationService: CachedNodeConfigurationService = {
- val cached = new CachedNodeConfigurationService(findExpectedRepo, nodeInfoServiceImpl)
+ val cached = new CachedNodeConfigurationService(findExpectedRepo, nodeFactInfoService)
cached.init().runOrDie(err => new RuntimeException(s"Error when initializing node configuration cache: " + err))
cached
}
@@ -3516,9 +3372,6 @@ object RudderConfigInit {
lazy val aggregateReportScheduler = new FindNewReportsExecution(executionService, RUDDER_REPORTS_EXECUTION_INTERVAL)
- // This needs to be done at the end, to be sure that all is initialized
- deploymentService.setDynamicsGroupsService(dyngroupUpdaterBatch)
-
// aggregate information about node count
// don't forget to start-it once out of the zone which lead to dead-lock (ie: in Lift boot)
lazy val historizeNodeCountBatch = for {
@@ -3543,24 +3396,94 @@ object RudderConfigInit {
_ <- cron.start
} yield ()
- lazy val checkInventoryUpdate = new CheckInventoryUpdate(
- nodeInfoServiceImpl,
- asyncDeploymentAgent,
- uuidGen,
- RUDDER_BATCH_CHECK_NODE_CACHE_INTERVAL
- )
- lazy val purgeDeletedInventories = new PurgeDeletedInventories(
- purgeDeletedNodes,
- FiniteDuration(RUDDER_BATCH_PURGE_DELETED_INVENTORIES_INTERVAL.toLong, "hours"),
- RUDDER_BATCH_PURGE_DELETED_INVENTORIES
- )
- lazy val purgeUnreferencedSoftwares = {
- new PurgeUnreferencedSoftwares(
- softwareService,
- FiniteDuration(RUDDER_BATCH_DELETE_SOFTWARE_INTERVAL.toLong, "hours")
+// provided as a callback on node fact repo
+// lazy val checkInventoryUpdate = new CheckInventoryUpdate(
+// nodeFactInfoService,
+// asyncDeploymentAgent,
+// uuidGen,
+// RUDDER_BATCH_CHECK_NODE_CACHE_INTERVAL
+// )
+
+ lazy val asynComplianceService = new AsyncComplianceService(reportingService)
+
+ /*
+ * here goes deprecated services that we can't remove yet, for example because they are used for migration
+ */
+ object deprecated {
+ lazy val ldapFullInventoryRepository =
+ new FullInventoryRepositoryImpl(inventoryDitService, inventoryMapper, rwLdap)
+
+ lazy val softwareInventoryDAO: ReadOnlySoftwareDAO =
+ new ReadOnlySoftwareDAOImpl(inventoryDitService, roLdap, inventoryMapper)
+
+ lazy val softwareInventoryRWDAO: WriteOnlySoftwareDAO = new WriteOnlySoftwareDAOImpl(
+ acceptedNodesDitImpl,
+ rwLdap
+ )
+
+ lazy val softwareService: SoftwareService =
+ new SoftwareServiceImpl(softwareInventoryDAO, softwareInventoryRWDAO, acceptedNodesDit)
+
+ lazy val purgeUnreferencedSoftwares = {
+ new PurgeUnreferencedSoftwares(
+ softwareService,
+ FiniteDuration(RUDDER_BATCH_DELETE_SOFTWARE_INTERVAL.toLong, "hours")
+ )
+ }
+
+ lazy val purgeDeletedInventories = new PurgeDeletedInventories(
+ new PurgeDeletedNodesImpl(rwLdap, removedNodesDitImpl, ldapFullInventoryRepository),
+ FiniteDuration(RUDDER_BATCH_PURGE_DELETED_INVENTORIES_INTERVAL.toLong, "hours"),
+ RUDDER_BATCH_PURGE_DELETED_INVENTORIES
+ )
+
+ lazy val ldapRemoveNodeBackend = new LdapRemoveNodeBackend(
+ nodeDitImpl,
+ pendingNodesDitImpl,
+ acceptedNodesDitImpl,
+ removedNodesDitImpl,
+ rwLdap,
+ ldapFullInventoryRepository,
+ nodeReadWriteMutex
+ )
+
+ lazy val ldapSoftwareSave = new NameAndVersionIdFinder("check_name_and_version", roLdap, inventoryMapper, acceptedNodesDit)
+
+ lazy val internalAcceptedQueryProcessor =
+ new InternalLDAPQueryProcessor(roLdap, acceptedNodesDitImpl, nodeDit, ditQueryDataImpl, ldapEntityMapper)
+
+ lazy val internalPendingQueryProcessor = {
+ val subGroup = new SubGroupComparatorRepository {
+ override def getNodeIds(groupId: NodeGroupId): IOResult[Chunk[NodeId]] = Chunk.empty.succeed
+
+ override def getGroups: IOResult[Chunk[SubGroupChoice]] = Chunk.empty.succeed
+ }
+ new InternalLDAPQueryProcessor(
+ roLdap,
+ pendingNodesDitImpl,
+ nodeDit,
+ // here, we don't want to look for subgroups to show them in the form => always return an empty list
+ new DitQueryData(
+ pendingNodesDitImpl,
+ nodeDit,
+ rudderDit,
+ new NodeQueryCriteriaData(() => subGroup)
+ ),
+ ldapEntityMapper
+ )
+ }
+
+ lazy val woLdapNodeRepository: WoNodeRepository = new WoLDAPNodeRepository(
+ nodeDitImpl,
+ acceptedNodesDit,
+ ldapEntityMapper,
+ rwLdap,
+ logRepository,
+ nodeReadWriteMutex,
+ cachedNodeConfigurationService,
+ reportingServiceImpl
)
}
- lazy val asynComplianceService = new AsyncComplianceService(reportingService)
// reference services part of the API
val rci = RudderServiceApi(
@@ -3571,14 +3494,14 @@ object RudderConfigInit {
rudderDit,
roLdapRuleRepository,
woRuleRepository,
- woNodeRepository,
+ woFactNodeRepository,
roLdapNodeGroupRepository,
woLdapNodeGroupRepository,
techniqueRepositoryImpl,
techniqueRepositoryImpl,
roLdapDirectiveRepository,
woLdapDirectiveRepository,
- softwareInventoryDAO,
+ deprecated.softwareInventoryDAO,
eventLogRepository,
eventLogDetailsServiceImpl,
reportingServiceImpl,
@@ -3590,9 +3513,7 @@ object RudderConfigInit {
ruleApplicationStatusImpl,
propertyEngineService,
newNodeManagerImpl,
- newNodeHookRunner,
nodeGridImpl,
- nodeSummaryServiceImpl,
jsTreeUtilServiceImpl,
directiveEditorService,
userPropertyService,
@@ -3601,29 +3522,28 @@ object RudderConfigInit {
policyServerManagementService,
dynGroupUpdaterService,
dyngroupUpdaterBatch,
- checkInventoryUpdate,
- purgeDeletedInventories,
- purgeUnreferencedSoftwares,
+ deprecated.purgeDeletedInventories,
+ deprecated.purgeUnreferencedSoftwares,
databaseManagerImpl,
dbCleaner,
techniqueLibraryUpdater,
autoReportLogger,
removeNodeServiceImpl,
- nodeInfoServiceImpl,
+ nodeFactInfoService,
reportDisplayerImpl,
dependencyAndDeletionService,
itemArchiveManagerImpl,
personIdentServiceImpl,
gitRevisionProviderImpl,
logDisplayerImpl,
- ldapFullInventoryRepository,
+ factFullInventoryRepo,
queryProcessor,
categoryHierarchyDisplayerImpl,
dynGroupServiceImpl,
ditQueryDataImpl,
reportsRepository,
eventLogDeploymentServiceImpl,
- new SrvGrid(roAgentRunsRepository, configService, roLdapRuleRepository, nodeInfoService),
+ new SrvGrid(roAgentRunsRepository, configService, roLdapRuleRepository, nodeFactInfoService),
findExpectedRepo,
roLDAPApiAccountRepository,
woLDAPApiAccountRepository,
@@ -3686,14 +3606,28 @@ object RudderConfigInit {
secretEventLogService,
changeRequestChangesSerialisation,
gitConfigRepo,
- gitModificationRepository
+ gitModificationRepository,
+ nodeFactRepository
)
+ // need to be done here to avoid cyclic dependencies
+ (nodeFactRepository.registerChangeCallbackAction(
+ new GenerationOnChange(updateDynamicGroups, asyncDeploymentAgent, uuidGen)
+ ) *>
+ nodeFactRepository.registerChangeCallbackAction(
+ new CacheInvalidateNodeFactEventCallback(cachedNodeConfigurationService, reportingServiceImpl, Nil)
+ )).runNow
+ // This needs to be done at the end, to be sure that all is initialized
+ deploymentService.setDynamicsGroupsService(dyngroupUpdaterBatch)
// we need to reference batches not part of the API to start them since
// they are lazy val
cleanOldInventoryBatch.start()
gitFactRepoGC.start()
gitConfigRepoGC.start()
+ // todo: scheduler interval should be a property
+ ZioRuntime.unsafeRun(jsonReportsAnalyzer.start(5.seconds).forkDaemon.provideLayer(ZioRuntime.layers))
+ ZioRuntime.unsafeRun(MainCampaignService.start(mainCampaignService))
+
// UpdateDynamicGroups is part of rci
// reportingServiceImpl part of rci
// checkInventoryUpdate part of rci
diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/checks/migration/MigrateNodeAcceptationInventories.scala b/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/checks/migration/MigrateNodeAcceptationInventories.scala
index b1771059191..4ada578fef1 100644
--- a/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/checks/migration/MigrateNodeAcceptationInventories.scala
+++ b/webapp/sources/rudder/rudder-web/src/main/scala/bootstrap/liftweb/checks/migration/MigrateNodeAcceptationInventories.scala
@@ -151,13 +151,16 @@ class MigrateNodeAcceptationInventories(
_.headOption match {
case None => ZIO.unit
case Some(v) =>
- for {
- last <- fileLogRepository.get(nodeId, v)
- opt <- nodeInfoService.getNodeInfo(nodeId)
- _ <- ZIO.when(opt.isDefined || last.datetime.plus(MAX_KEEP_REFUSED.toMillis).isAfter(now)) {
- saveInDB(nodeId, last.datetime, last.data, !opt.isDefined)
- }
- } yield ()
+ fileLogRepository.get(nodeId, v).flatMap {
+ case None => ZIO.unit
+ case Some(l) =>
+ for {
+ opt <- nodeInfoService.getNodeInfo(nodeId)
+ _ <- ZIO.when(opt.isDefined || l.datetime.plus(MAX_KEEP_REFUSED.toMillis).isAfter(now)) {
+ saveInDB(nodeId, l.datetime, l.data, !opt.isDefined)
+ }
+ } yield ()
+ }
}
} *> purgeLogFile(nodeId)
}
diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/components/SearchNodeComponent.scala b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/components/SearchNodeComponent.scala
index c8e743f6180..de051cad280 100644
--- a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/components/SearchNodeComponent.scala
+++ b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/components/SearchNodeComponent.scala
@@ -682,7 +682,7 @@ object SearchNodeComponent {
)
}
- val defaultLine: CriterionLine = {
+ val defaultLine = {
// in case of further modification in ditQueryData
require(
ditQueryData.criteriaMap(OC_NODE).criteria(0).name == "OS",
diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/services/DisplayNode.scala b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/services/DisplayNode.scala
index fa862847bba..4e6ac10e2c7 100644
--- a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/services/DisplayNode.scala
+++ b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/services/DisplayNode.scala
@@ -52,6 +52,8 @@ import com.normation.rudder.domain.policies.PolicyModeOverrides._
import com.normation.rudder.domain.reports.ComplianceLevel
import com.normation.rudder.domain.reports.ComplianceLevelSerialisation
import com.normation.rudder.domain.reports.NodeStatusReport
+import com.normation.rudder.facts.nodes.ChangeContext
+import com.normation.rudder.facts.nodes.SelectFacts
import com.normation.rudder.hooks.HookReturnCode
import com.normation.rudder.services.reports.NoReportInInterval
import com.normation.rudder.services.reports.Pending
@@ -91,7 +93,7 @@ import scala.xml.Utility.escape
*/
object DisplayNode extends Loggable {
- private[this] val getSoftwareService = RudderConfig.readOnlySoftwareDAO
+ private[this] val nodeFactRepository = RudderConfig.nodeFactRepository
private[this] val removeNodeService = RudderConfig.removeNodeService
private[this] val asyncDeploymentAgent = RudderConfig.asyncDeploymentAgent
private[this] val uuidGen = RudderConfig.stringUuidGenerator
@@ -145,9 +147,10 @@ object DisplayNode extends Loggable {
}
}
- private def loadSoftware(jsId: JsNodeId, softIds: Seq[SoftwareUuid])(nodeId: String): JsCmd = {
+ private def loadSoftware(jsId: JsNodeId)(nodeId: String): JsCmd = {
+ implicit val attrs = SelectFacts.none.copy(software = SelectFacts.none.software.toRetrieve)
(for {
- seq <- getSoftwareService.getSoftware(softIds)
+ seq <- nodeFactRepository.slowGet(NodeId(nodeId)).map(_.toList.flatMap(_.software.map(_.toSoftware)))
gridDataId = htmlId(jsId, "soft_grid_data_")
gridId = "soft"
} yield SetExp(
@@ -288,7 +291,7 @@ object DisplayNode extends Loggable {
// if the firstChild.id == softGridId, then it hasn't been loaded, otherwise it is softGridId_wrapper
JsRaw(s"""
$$("#${softPanelId}").click(function() {
- ${SHtml.ajaxCall(JsRaw("'" + nodeId.value + "'"), loadSoftware(jsId, softIds))._2.toJsCmd}
+ ${SHtml.ajaxCall(JsRaw("'" + nodeId.value + "'"), loadSoftware(jsId))._2.toJsCmd}
});
""")
)
@@ -681,9 +684,9 @@ object DisplayNode extends Loggable {
case None => NodeSeq.Empty
case Some(machine) => (
machine.machineType match {
+ case UnknownMachineType => Text("Unknown machine type")
case PhysicalMachineType => Text("Physical machine")
case VirtualMachineType(vmType) => Text("Virtual machine (%s)".format(S.?("vm.type." + vmType.name)))
- case UnknownMachineType => Text("Unknown machine type")
}
)
}
@@ -1145,12 +1148,18 @@ object DisplayNode extends Loggable {
}
private[this] def removeNode(node: NodeSummary): JsCmd = {
- val modId = ModificationId(uuidGen.newUuid)
- removeNodeService
- .removeNodePure(node.id, DeleteMode.Erase, modId, CurrentUser.actor) // only erase for Rudder 8.0
- .toBox match {
+ implicit val cc: ChangeContext = ChangeContext(
+ ModificationId(uuidGen.newUuid),
+ CurrentUser.actor,
+ DateTime.now(),
+ None,
+ S.request.map(_.remoteAddr).toOption
+ )
+
+ // only erase for Rudder 8.0
+ removeNodeService.removeNodePure(node.id, DeleteMode.Erase).toBox match {
case Full(_) =>
- asyncDeploymentAgent ! AutomaticStartDeployment(modId, CurrentUser.actor)
+ asyncDeploymentAgent ! AutomaticStartDeployment(cc.modId, cc.actor)
onSuccess(node)
case eb: EmptyBox =>
val message = s"There was an error while deleting node '${node.hostname}' [${node.id.value}]"
diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/services/NodeGrid.scala b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/services/NodeGrid.scala
index 0fb3bbae5b7..c91de105bf6 100644
--- a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/services/NodeGrid.scala
+++ b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/services/NodeGrid.scala
@@ -41,7 +41,7 @@ import com.normation.appconfig.ReadConfigService
import com.normation.box._
import com.normation.inventory.domain.InventoryStatus
import com.normation.inventory.domain.NodeId
-import com.normation.inventory.ldap.core._
+import com.normation.inventory.services.core.FullInventoryRepository
import com.normation.rudder.domain.servers.Srv
import com.normation.rudder.services.nodes.NodeInfoService
import com.normation.rudder.web.ChooseTemplate
@@ -77,7 +77,7 @@ final case class JsonArg(jsid: String, id: String, status: String)
* - call the display(servers) method
*/
final class NodeGrid(
- getNodeAndMachine: LDAPFullInventoryRepository,
+ getNodeAndMachine: FullInventoryRepository[_],
nodeInfoService: NodeInfoService,
configService: ReadConfigService
) extends Loggable {
diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/AcceptNode.scala b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/AcceptNode.scala
index 7ac783b9bbd..152aa794fbf 100644
--- a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/AcceptNode.scala
+++ b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/AcceptNode.scala
@@ -42,9 +42,11 @@ import com.normation.box._
import com.normation.eventlog.EventActor
import com.normation.eventlog.ModificationId
import com.normation.inventory.domain.NodeId
-import com.normation.inventory.domain.PendingInventory
import com.normation.rudder.domain.logger.TimingDebugLogger
import com.normation.rudder.domain.servers.Srv
+import com.normation.rudder.facts.nodes.ChangeContext
+import com.normation.rudder.facts.nodes.CoreNodeFact
+import com.normation.rudder.facts.nodes.SelectNodeStatus
import com.normation.rudder.web.ChooseTemplate
import com.normation.rudder.web.components.popup.ExpectedPolicyPopup
import com.normation.rudder.web.services.CurrentUser
@@ -60,6 +62,7 @@ import org.joda.time.DateTime
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.core.userdetails.UserDetails
import scala.xml._
+import zio.stream.ZSink
/**
* Check for server in the pending repository and propose to
@@ -68,10 +71,10 @@ import scala.xml._
*/
class AcceptNode extends Loggable {
- val newNodeManager = RudderConfig.newNodeManager
- val rudderDit = RudderConfig.rudderDit
- val serverGrid = RudderConfig.nodeGrid
- val serverSummaryService = RudderConfig.nodeSummaryService
+ val newNodeManager = RudderConfig.newNodeManager
+ val rudderDit = RudderConfig.rudderDit
+ val serverGrid = RudderConfig.nodeGrid
+ val nodeFactRepository = RudderConfig.nodeFactRepository
val historyRepos = RudderConfig.inventoryHistoryJdbcRepository
val logRepository = RudderConfig.eventLogRepository
@@ -113,7 +116,7 @@ class AcceptNode extends Loggable {
def list(html: NodeSeq): NodeSeq = {
- newNodeManager.listNewNodes match {
+ newNodeManager.listNewNodes.toBox match {
case Empty => Error, no server found
case f @ Failure(_, _, _) => Error while retrieving pending nodes list
case Full(seq) => display(html, seq)
@@ -141,8 +144,18 @@ class AcceptNode extends Loggable {
// TODO : manage error message
S.clearCurrentNotices
listNode.foreach { id =>
+ implicit val cc: ChangeContext = {
+ ChangeContext(
+ modId,
+ CurrentUser.actor,
+ DateTime.now(),
+ None,
+ S.request.map(_.remoteAddr).toOption
+ )
+ }
val now = System.currentTimeMillis
- val accept = newNodeManager.accept(id, modId, CurrentUser.actor)
+ val accept =
+ newNodeManager.accept(id).toBox
if (TimingDebugLogger.isDebugEnabled) {
TimingDebugLogger.debug(s"Accepting node ${id.value}: ${System.currentTimeMillis - now}ms")
}
@@ -168,7 +181,9 @@ class AcceptNode extends Loggable {
S.clearCurrentNotices
val modId = ModificationId(uuidGen.newUuid)
listNode.foreach { id =>
- newNodeManager.refuse(id, modId, CurrentUser.actor) match {
+ newNodeManager
+ .refuse(id)(ChangeContext(modId, CurrentUser.actor, DateTime.now(), None, S.request.map(_.remoteAddr).toOption))
+ .toBox match {
case e: EmptyBox =>
logger.error(s"Refuse node '${id.value}' lead to Failure.", e)
S.error(Error while refusing node(s).)
@@ -233,7 +248,11 @@ class AcceptNode extends Loggable {
"#server_os *" #> srv.osFullName)(serverLine)
}
- serverSummaryService.find(PendingInventory, listNode: _*) match {
+ nodeFactRepository
+ .getAll()(SelectNodeStatus.Pending)
+ .collect { case n if (listNode.contains(n.id)) => n.toSrv }
+ .run(ZSink.collectAll)
+ .toBox match {
case Full(servers) =>
val lines: NodeSeq = servers.flatMap(displayServerLine)
("#server_lines" #> lines).apply(
@@ -295,10 +314,10 @@ class AcceptNode extends Loggable {
OnLoad(JsRaw("""createPopup("expectedPolicyPopup")"""))
}
- def display(html: NodeSeq, nodes: Seq[Srv]): NodeSeq = {
+ def display(html: NodeSeq, nodes: Seq[CoreNodeFact]): NodeSeq = {
val servers = {
serverGrid.displayAndInit(
- nodes,
+ nodes.map(_.toSrv),
"acceptNodeGrid",
Seq(
(Text("Since"), { e => Text(DateFormaterService.getDisplayDate(e.creationDate)) }),
diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/NodeHistoryViewer.scala b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/NodeHistoryViewer.scala
index f7826aa1442..bc2d8da23f2 100644
--- a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/NodeHistoryViewer.scala
+++ b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/NodeHistoryViewer.scala
@@ -75,9 +75,9 @@ class NodeHistoryViewer extends StatefulSnippet {
{SHtml.ajaxSelectObj[DateTime](dates, Full(selectedDate), onSelect _)}
{
historyRepos.get(uuid, selectedDate).toBox match {
- case Failure(m, _, _) => Error while trying to display node history. Error message: {m}
- case Empty => No history was retrieved for the chosen date
- case Full(sm) =>
+ case Failure(m, _, _) => Error while trying to display node history. Error message: {m}
+ case Empty | Full(None) => No history was retrieved for the chosen date
+ case Full(Some(sm)) =>
{
DisplayNode.showPannedContent(None, sm.data.fact.toFullInventory, sm.data.status, "hist") ++
Script(DisplayNode.jsInit(sm.id, sm.data.fact.toFullInventory.node.softwareIds, "hist"))
@@ -117,9 +117,9 @@ class NodeHistoryViewer extends StatefulSnippet {
private def onSelect(date: DateTime): JsCmd = {
historyRepos.get(uuid, date).toBox match {
- case Failure(m, _, _) => Alert("Error while trying to display node history. Error message:" + m)
- case Empty => Alert("No history was retrieved for the chosen date")
- case Full(sm) =>
+ case Failure(m, _, _) => Alert("Error while trying to display node history. Error message:" + m)
+ case Empty | Full(None) => Alert("No history was retrieved for the chosen date")
+ case Full(Some(sm)) =>
SetHtml(hid, DisplayNode.showPannedContent(None, sm.data.fact.toFullInventory, sm.data.status, "hist")) &
DisplayNode.jsInit(sm.id, sm.data.fact.toFullInventory.node.softwareIds, "hist")
}
diff --git a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/PendingHistoryGrid.scala b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/PendingHistoryGrid.scala
index 01e5f54b2cc..584c9d57868 100644
--- a/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/PendingHistoryGrid.scala
+++ b/webapp/sources/rudder/rudder-web/src/main/scala/com/normation/rudder/web/snippet/node/PendingHistoryGrid.scala
@@ -136,7 +136,7 @@ object PendingHistoryGrid extends Loggable {
("tr [jsuuid]" #> jsuuid &
"tr [serveruuid]" #> details.nodeId.value &
"tr [kind]" #> status.toLowerCase &
- "tr [inventory]" #> details.inventoryVersion.toString() &
+ "tr [inventory]" #> DateFormaterService.serialize(event.creationDate) &
".date *" #> DateFormaterService.getDisplayDate(event.creationDate) &
".name *" #> details.hostname &
".os *" #> details.fullOsName &
@@ -220,9 +220,9 @@ object PendingHistoryGrid extends Loggable {
val version = ISODateTimeFormat.dateTimeParser.parseDateTime(arr(2))
val isAcceptLine = arr(3) == "accepted"
history.get(id, version).toBox match {
- case Failure(m, _, _) => Alert("Error while trying to display node history. Error message:" + m)
- case Empty => Alert("No history was retrieved for the chosen date")
- case Full(sm) =>
+ case Failure(m, _, _) => Alert("Error while trying to display node history. Error message:" + m)
+ case Empty | Full(None) => Alert("No history was retrieved for the chosen date")
+ case Full(Some(sm)) =>
SetHtml(
jsuuid,
(if (isAcceptLine)
diff --git a/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateNodeAcceptationInventories.scala b/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateNodeAcceptationInventories.scala
index 2d9f2e7d22a..886f69c4047 100644
--- a/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateNodeAcceptationInventories.scala
+++ b/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateNodeAcceptationInventories.scala
@@ -185,11 +185,20 @@ trait TestMigrateNodeAcceptationInventories extends Specification with AfterAll
} yield files.toSeq.map(f => NodeId(f.name))
}
- override def get(id: NodeId, version: DateTime): IOResult[FactLog] = {
- for {
- json <- IOResult.attempt(s"Read json for ${id.value}}")(factFile(id, version).contentAsString)
- fact <- json.fromJson[NodeFact].toIO
- } yield FactLog(id, version, FactLogData(fact, EventActor("rudder-migration"), AcceptedInventory))
+ override def get(id: NodeId, version: DateTime): IOResult[Option[FactLog]] = {
+ val file = factFile(id, version)
+ ZIO
+ .whenZIO(IOResult.attempt(file.exists)) {
+ IOResult.attempt(s"Read json for ${id.value}}")(file.contentAsString)
+ }
+ .flatMap {
+ case Some(json) =>
+ for {
+ fact <- json.fromJson[NodeFact].toIO
+ } yield Some(FactLog(id, version, FactLogData(fact, EventActor("rudder-migration"), AcceptedInventory)))
+ case None =>
+ None.succeed
+ }
}
override def versions(id: NodeId): IOResult[Seq[DateTime]] = {
@@ -254,7 +263,7 @@ trait TestMigrateNodeAcceptationInventories extends Specification with AfterAll
}
override def getNodeInfos(nodeIds: Set[NodeId]): IOResult[Set[NodeInfo]] = ???
override def getNodeInfosSeq(nodesId: Seq[NodeId]): IOResult[Seq[NodeInfo]] = ???
- override def getNumberOfManagedNodes: Int = ???
+ override def getNumberOfManagedNodes: IOResult[Int] = ???
override def getAllNodesIds(): IOResult[Set[NodeId]] = ???
override def getAllNodes(): IOResult[Map[NodeId, Node]] = ???
override def getAllSystemNodeIds(): IOResult[Seq[NodeId]] = ???
diff --git a/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateSystemTechnique7_0.scala b/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateSystemTechnique7_0.scala
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/webapp/sources/rudder/rudder-web/src/test/scala/bootstrap/liftweb/checks/migration/TestMigrateSystemTechnique7_0.scala
@@ -0,0 +1 @@
+
diff --git a/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/LDAPConnection.scala b/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/LDAPConnection.scala
index 6bc1aa871d6..d6ae1886d23 100644
--- a/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/LDAPConnection.scala
+++ b/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/LDAPConnection.scala
@@ -528,27 +528,32 @@ class RwLDAPConnection(
* better than us for orchestrating its changes.
*
*/
- private def applyMod[MOD <: ReadOnlyLDAPRequest](
+ def applyMod[MOD <: ReadOnlyLDAPRequest](
modName: String,
toLDIFChangeRecord: MOD => LDIFChangeRecord,
backendAction: MOD => LDAPResult,
onlyReportThat: ResultCode => Boolean
)(req: MOD): LDAPIOResult[LDIFChangeRecord] = {
val record = toLDIFChangeRecord(req)
- blocking {
- ldifFileLogger.records(Seq(record)) // ignore return value
- backendAction(req)
- } flatMap { res =>
- if (res.getResultCode == SUCCESS) {
- record.succeed
- } else if (onlyReportThat(res.getResultCode)) {
- LDIFNoopChangeRecord(record.getParsedDN).succeed
- } else {
- LDAPRudderError
- .FailureResult(s"Error when doing action '${modName}' with and LDIF change request: ${res.getDiagnosticMessage}", res)
- .fail
- }
- } catchAll { x =>
+ ZIO
+ .blocking(blocking {
+ ldifFileLogger.records(Seq(record)) // ignore return value
+ backendAction(req)
+ })
+ .flatMap { res =>
+ (if (res.getResultCode == SUCCESS) {
+ record.succeed
+ } else if (onlyReportThat(res.getResultCode)) {
+ LDIFNoopChangeRecord(record.getParsedDN).succeed
+ } else {
+ LDAPRudderError
+ .FailureResult(
+ s"Error when doing action '${modName}' with and LDIF change request: ${res.getDiagnosticMessage}",
+ res
+ )
+ .fail
+ })
+ } catchAll { x =>
(x: @unchecked) match {
case ex: LDAPException =>
if (onlyReportThat(ex.getResultCode)) {
@@ -591,7 +596,7 @@ class RwLDAPConnection(
/**
* Specialized version of applyMods for AddRequest modification type
*/
- private val applyAdds = applyMods[AddRequest](
+ val applyAdds = applyMods[AddRequest](
"adds",
(req: AddRequest) => req.toLDIFChangeRecord,
(req: AddRequest) => backed.add(req),
diff --git a/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/LDAPConnectionProvider.scala b/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/LDAPConnectionProvider.scala
index 19dc8329c90..0d08dd3e606 100644
--- a/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/LDAPConnectionProvider.scala
+++ b/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/LDAPConnectionProvider.scala
@@ -72,14 +72,14 @@ trait LDAPConnectionProvider[LDAP <: RoLDAPConnection] {
* return type is Unit.
*/
def foreach(f: LDAP => Unit): IOResult[Unit] = {
- withConLdap[Unit] { con => f(con); ZIO.unit }
+ withConLdap[Unit](con => LDAPIOResult.attempt(f(con)).unit)
}
/**
* map on connection provider
*/
def map[A](f: LDAP => A): LDAPIOResult[A] = {
- withConLdap[A](con => f(con).succeed)
+ withConLdap[A](con => LDAPIOResult.attempt(f(con)))
}
/**
diff --git a/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/schema/LDAPObjectClass.scala b/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/schema/LDAPObjectClass.scala
index bf6368846e8..48cf2f9fde3 100644
--- a/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/schema/LDAPObjectClass.scala
+++ b/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/schema/LDAPObjectClass.scala
@@ -33,7 +33,6 @@ final case class LDAPObjectClass(
val attributes = mustAttr ++ mayAttr
assert(null != name && name.nonEmpty, "Name can't be null or empty")
assert(attributes.forall(a => null != a && a.nonEmpty), "Attributes name can't be null or empty")
-
}
object LDAPObjectClass {
diff --git a/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/schema/LDAPSchema.scala b/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/schema/LDAPSchema.scala
index 752348811a2..eb285757494 100644
--- a/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/schema/LDAPSchema.scala
+++ b/webapp/sources/scala-ldap/src/main/scala/com/normation/ldap/sdk/schema/LDAPSchema.scala
@@ -99,7 +99,11 @@ class LDAPSchema {
sup: LDAPObjectClass = LDAPObjectClass.TOP,
must: Set[String] = Set(),
may: Set[String] = Set()
- ): LDAPSchema = addObjectClass(new LDAPObjectClass(name, sup, must, may))
+ ): LDAPObjectClass = {
+ val oc = new LDAPObjectClass(name, sup, must, may)
+ addObjectClass(oc)
+ oc
+ }
/**
* Returned the set of children object classes for the
diff --git a/webapp/sources/scala-ldap/src/test/scala/com/normation/ldap/sdk/schema/SchemaTest.scala b/webapp/sources/scala-ldap/src/test/scala/com/normation/ldap/sdk/schema/SchemaTest.scala
index ab7d7dbc548..06a224dbad5 100644
--- a/webapp/sources/scala-ldap/src/test/scala/com/normation/ldap/sdk/schema/SchemaTest.scala
+++ b/webapp/sources/scala-ldap/src/test/scala/com/normation/ldap/sdk/schema/SchemaTest.scala
@@ -42,13 +42,13 @@ class SchemaTest extends Specification {
* `- L1_2_0
*/
OC.createObjectClass("L1_0")
- .createObjectClass("L1_1")
- .createObjectClass("L1_2")
- .createObjectClass("L1_0_0", sup = OC("L1_0"))
- .createObjectClass("L1_0_1", sup = OC("L1_0"))
- .createObjectClass("L1_0_2", sup = OC("L1_0"))
- .createObjectClass("L1_0_0_0", sup = OC("L1_0_0"))
- .createObjectClass("L1_2_0", sup = OC("L1_2"))
+ OC.createObjectClass("L1_1")
+ OC.createObjectClass("L1_2")
+ OC.createObjectClass("L1_0_0", sup = OC("L1_0"))
+ OC.createObjectClass("L1_0_1", sup = OC("L1_0"))
+ OC.createObjectClass("L1_0_2", sup = OC("L1_0"))
+ OC.createObjectClass("L1_0_0_0", sup = OC("L1_0_0"))
+ OC.createObjectClass("L1_2_0", sup = OC("L1_2"))
"top" >> (
(OC.demux() must beEmpty) and