Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
fanf committed Jun 4, 2018
1 parent 8febb18 commit 766b309
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ import net.liftweb.json._
import JsonDSL._
import com.normation.rudder.domain.RudderLDAPConstants.A_STATE
import com.normation.rudder.domain.nodes.NodeGroupId
import com.normation.rudder.domain.nodes.NodeInfo
import com.normation.rudder.domain.nodes.NodeState
import com.normation.utils.HashcodeCaching
import com.normation.rudder.services.queries._
Expand Down Expand Up @@ -138,7 +139,57 @@ sealed trait CriterionType extends ComparatorList {
}

protected def validateSubCase(value:String,comparator:CriterionComparator) : Box[String]
}


/*
* There is two kinds of comparators:
* - the ones working on inventories/LDAP data, which needs to defined how to transform
* the criterion into an LDAP filter,
* - the ones working on Rudder NodeInfo, which are higher level and works on information
* provided by NodeInfoService
*/

/*
* Below goes all NodeInfo Criterion Type
*/
sealed trait NodeCriterionType extends CriterionType {

def matches(node: NodeInfo, comparator: CriterionComparator, value: String): Boolean
}


case object NodeStateComparator extends NodeCriterionType {

//this need to be lazy, else access to "S." at boot will lead to NPE.
lazy val nodeStates = NodeState.labeledPairs.map{ case (x, label) => (x.name, label) }

override def comparators = Seq(Equals, NotEquals)
override protected def validateSubCase(v: String, comparator:CriterionComparator) = {
if (null == v || v.length == 0) Failure("Empty string not allowed") else Full(v)
}

override def matches(node: NodeInfo, comparator: CriterionComparator, value: String): Boolean = {
comparator match {
case Equals => node.state.name == value
case _ => node.state.name != value
}
}

override def toForm(value: String, func: String => Any, attrs: (String, String)*) : Elem =
SHtml.select(
nodeStates
, Box(nodeStates.find( _._1 == value).map(_._1))
, func
, attrs:_*
)
}

/*
* Below goes all LDAP Criterion Type
*/

sealed trait LDAPCriterionType extends CriterionType {
//transform the given value to its LDAP string value
def toLDAP(value:String) : Box[String]

Expand All @@ -164,14 +215,14 @@ sealed trait CriterionType extends ComparatorList {
}

//a comparator type with undefined comparators
case class BareComparator(override val comparators:CriterionComparator*) extends CriterionType with HashcodeCaching {
case class BareComparator(override val comparators: CriterionComparator*) extends LDAPCriterionType with HashcodeCaching {
override protected def validateSubCase(v:String,comparator:CriterionComparator) = Full(v)
override def toLDAP(value:String) = Full(value)
}

trait TStringComparator extends CriterionType {
trait TStringComparator extends LDAPCriterionType {

override protected def validateSubCase(v:String,comparator:CriterionComparator) = {
override protected def validateSubCase(v: String, comparator: CriterionComparator) = {
if(null == v || v.length == 0) Failure("Empty string not allowed") else {
comparator match {
case Regex | NotRegex =>
Expand Down Expand Up @@ -234,7 +285,7 @@ case object OrderedStringComparator extends TStringComparator {
}
}

case object DateComparator extends CriterionType {
case object DateComparator extends LDAPCriterionType {
override val comparators = OrderedComparators.comparators.filterNot( c => c == Regex || c == NotRegex)
val fmt = "dd/MM/yyyy"
val frenchFmt = DateTimeFormat.forPattern(fmt).withLocale(Locale.FRANCE)
Expand Down Expand Up @@ -292,7 +343,7 @@ case object DateComparator extends CriterionType {

}

case object BooleanComparator extends CriterionType {
case object BooleanComparator extends LDAPCriterionType {
override val comparators = BaseComparators.comparators
override protected def validateSubCase(v:String,comparator:CriterionComparator) = v.toLowerCase match {
case "t" | "f" | "true" | "false" => Full(v)
Expand All @@ -304,7 +355,7 @@ case object BooleanComparator extends CriterionType {
}
}

case object LongComparator extends CriterionType {
case object LongComparator extends LDAPCriterionType {
override val comparators = OrderedComparators.comparators
override protected def validateSubCase(v:String,comparator:CriterionComparator) = try {
Full((v.toLong).toString)
Expand All @@ -318,7 +369,7 @@ case object LongComparator extends CriterionType {
}
}

case object MemoryComparator extends CriterionType {
case object MemoryComparator extends LDAPCriterionType {
override val comparators = OrderedComparators.comparators
override protected def validateSubCase(v:String,comparator:CriterionComparator) = {
if(MemorySize.parse(v).isDefined) Full(v)
Expand All @@ -331,35 +382,8 @@ case object MemoryComparator extends CriterionType {
}
}

case object NodeStateComparator extends CriterionType {

//this need to be lazy, else access to "S." at boot will lead to NPE.
lazy val nodeStates = NodeState.labeledPairs.map{ case (x, label) => (x.name, label) }

override def comparators = Seq(Equals, NotEquals)
override protected def validateSubCase(v: String, comparator:CriterionComparator) = {
if (null == v || v.length == 0) Failure("Empty string not allowed") else Full(v)
}

override def toLDAP(value: String) = Full(value.toLowerCase)

override def buildFilter(attributeName: String, comparator: CriterionComparator, value: String): Filter = {
comparator match {
case Equals => EQ(A_STATE, value)
case _ => NOT(EQ(A_STATE, value))
}
}

override def toForm(value: String, func: String => Any, attrs: (String, String)*) : Elem =
SHtml.select(
nodeStates
, Box(nodeStates.find( _._1 == value).map(_._1))
, func
, attrs:_*
)
}

case object MachineComparator extends CriterionType {
case object MachineComparator extends LDAPCriterionType {

val machineTypes = "Virtual" :: "Physical" :: Nil

Expand Down Expand Up @@ -391,7 +415,7 @@ case object MachineComparator extends CriterionType {
)
}

case object OstypeComparator extends CriterionType {
case object OstypeComparator extends LDAPCriterionType {
val osTypes = List("AIX", "BSD", "Linux", "Solaris", "Windows")
override def comparators = Seq(Equals, NotEquals)
override protected def validateSubCase(v:String,comparator:CriterionComparator) = {
Expand Down Expand Up @@ -424,7 +448,7 @@ case object OstypeComparator extends CriterionType {
)
}

case object OsNameComparator extends CriterionType {
case object OsNameComparator extends LDAPCriterionType {
import net.liftweb.http.S

val osNames = AixOS ::
Expand Down Expand Up @@ -477,7 +501,7 @@ case object OsNameComparator extends CriterionType {
*
* So we do actually need a special agent type "cfengine", and hand craft the buildFilter for it.
*/
case object AgentComparator extends CriterionType {
case object AgentComparator extends LDAPCriterionType {

val ANY_CFENGINE = "cfengine"
val (cfeTypes, cfeAgents) = ((ANY_CFENGINE, "Any CFEngine based agent"),(ANY_CFENGINE, AgentType.CfeCommunity :: AgentType.CfeEnterprise :: Nil))
Expand Down Expand Up @@ -531,7 +555,7 @@ case object AgentComparator extends CriterionType {
)
}

case object EditorComparator extends CriterionType {
case object EditorComparator extends LDAPCriterionType {
val editors = List("Microsoft", "RedHat", "Debian", "Adobe", "Macromedia")
override val comparators = BaseComparators.comparators
override protected def validateSubCase(v:String,comparator:CriterionComparator) =
Expand Down Expand Up @@ -786,20 +810,13 @@ class SubGroupComparator(getGroups: () => Box[Seq[SubGroupChoice]]) extends TStr

/**
* Create a new criterion for the given attribute `name`, and `cType` comparator.
* Optionnaly, you can provide an override for the object type for which that
* criterion is looked up.
* It is necessary when you want to make the criterion appears under a given object type,
* but it is really in one other (for example: inventory node vs rudder node).
* Optionnaly, you can provide an override to signal that that criterion is not
* on an inventory (or purelly on an inventory) property but on a RudderNode property.
* In that case, give the predicat that the node must follows.
*/
case class Criterion(val name:String, val cType:CriterionType, overrideObjectType: Option[String] = None) extends HashcodeCaching {
case class Criterion(val name:String, val cType: CriterionType, overrideObjectType: Option[String] = None) extends HashcodeCaching {
require(name != null && name.length > 0, "Criterion name must be defined")
require(cType != null, "Criterion Type must be defined")

def buildRegex(attribute:String,value:String) = cType.buildRegex(attribute,value)

def buildNotRegex(attribute:String,value:String) = cType.buildNotRegex(attribute,value)

def buildFilter(comp:CriterionComparator,value:String) = cType.buildFilter(name,comp,value)
}

case class ObjectCriterion(val objectType:String, val criteria:Seq[Criterion]) extends HashcodeCaching {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import com.normation.ldap.sdk.LDAPConnectionProvider
import com.normation.inventory.ldap.core.InventoryDit
import com.normation.rudder.domain.NodeDit
import net.liftweb.common._
import com.normation.rudder.domain.nodes.{NodeInfo, Node}
import com.normation.rudder.domain.nodes.{Node, NodeInfo}
import com.normation.rudder.domain.RudderLDAPConstants._
import com.normation.inventory.ldap.core.LDAPConstants._
import com.unboundid.ldap.sdk._
Expand All @@ -55,6 +55,9 @@ import com.normation.rudder.domain.logger.TimingDebugLogger
import com.normation.rudder.repository.CachedRepository
import com.normation.inventory.ldap.core.InventoryDit
import com.normation.inventory.ldap.core.InventoryMapper
import com.normation.rudder.domain.queries.And
import com.normation.rudder.domain.queries.CriterionComposition
import com.normation.rudder.domain.queries.Or

/*
* General logic for the cache implementation of NodeInfo.
Expand Down Expand Up @@ -97,9 +100,10 @@ final case class LDAPNodeInfo(
trait NodeInfoService {

/**
* Retrieve minimal information needed for the node info
* Retrieve minimal information needed for the node info, used (only) by the
* LDAP QueryProcessor.
*/
def getLDAPNodeInfo(nodeIds: Set[NodeId]) : Box[Set[LDAPNodeInfo]]
def getLDAPNodeInfo(nodeIds: Set[NodeId], predicates: Seq[NodeInfo => Boolean], composition: CriterionComposition) : Box[Set[LDAPNodeInfo]]

/**
* Return a NodeInfo from a NodeId. First check the ou=Node, then fetch the other data
Expand All @@ -108,14 +112,6 @@ trait NodeInfoService {
*/
def getNodeInfo(nodeId: NodeId) : Box[Option[NodeInfo]]

/**
* Get the node (not inventory).
* Most of the info are also in node info,
* but for some specific case (nodeProperties for ex),
* we need them.
*/
def getNode(nodeId: NodeId): Box[Node]

/**
* Get all node infos.
* That method try to return the maximum
Expand Down Expand Up @@ -468,22 +464,30 @@ trait NodeInfoServiceCached extends NodeInfoService with Loggable with CachedRep
Full(cache.mapValues(_._2.node))
}

def getLDAPNodeInfo(nodeIds: Set[NodeId]): Box[Set[LDAPNodeInfo]] = {
if(nodeIds.size > 0) {
withUpToDateCache(s"${nodeIds.size} ldap node info") { cache =>
Full(cache.collect { case(k, (x,_)) if(nodeIds.contains(k)) => x }.toSet)
}
} else {
Full(Set())
def getLDAPNodeInfo(nodeIds: Set[NodeId], predicats: Seq[NodeInfo => Boolean], composition: CriterionComposition): Box[Set[LDAPNodeInfo]] = {
def comp(a: Boolean, b: Boolean) = composition match {
case And => a && b
case Or => a || b
}
// combine predicats according to comp
def combine(a: NodeInfo => Boolean, b: NodeInfo => Boolean) = (n:NodeInfo) => comp(a(n), b(n))

val p = (if(predicats.isEmpty) {
composition match {
case And => Seq((_:NodeInfo) => true )
case Or => Seq((_:NodeInfo) => false)
}
} else(predicats)
).reduceLeft(combine)

withUpToDateCache(s"${nodeIds.size} ldap node info") { cache =>
Full(cache.collect { case(k, (x,y)) if(comp(nodeIds.contains(k), p(y))) => x }.toSet)
}
}
def getNode(nodeId: NodeId): Box[Node] = withUpToDateCache(s"${nodeId.value} node") { cache =>
Box(cache.get(nodeId).map( _._2.node)) ?~! s"Node with ID '${nodeId.value}' was not found"
}

def getNodeInfo(nodeId: NodeId): Box[Option[NodeInfo]] = withUpToDateCache(s"${nodeId.value} node info") { cache =>
Full(cache.get(nodeId).map( _._2))
}

}

/**
Expand Down
Loading

0 comments on commit 766b309

Please sign in to comment.