Revision f16bbcd3
Added by François ARMAND over 6 years ago
inventory-api/src/main/scala/com/normation/inventory/domain/DataTypes.scala | ||
---|---|---|
* Comparison are really important in Version
|
||
*/
|
||
final class Version(val value:String) extends Comparable[Version] {
|
||
require(nonEmpty(value))
|
||
require(!isEmpty(value))
|
||
|
||
override def compareTo(other:Version) = this.value.compareTo(other.value)
|
||
override def toString() = "[%s]".format(value)
|
inventory-api/src/main/scala/com/normation/inventory/domain/Identifiers.scala | ||
---|---|---|
|
||
trait Uuid {
|
||
val value:String
|
||
require(nonEmpty(value), "An UUID can not have a null or empty value (value: %s)".format(value))
|
||
require(!isEmpty(value), "An UUID can not have a null or empty value (value: %s)".format(value))
|
||
}
|
||
|
||
case class NodeId(val value:String) extends Uuid with HashcodeCaching
|
inventory-api/src/main/scala/com/normation/inventory/domain/MemorySize.scala | ||
---|---|---|
|
||
package com.normation.inventory.domain
|
||
|
||
import com.normation.exceptions.TechnicalException
|
||
import com.normation.utils.HashcodeCaching
|
||
|
||
|
||
... | ... | |
}
|
||
|
||
object MemorySize {
|
||
import scala.util.control.Exception._
|
||
/*
|
||
* We should accept:
|
||
* - no decimal numbers
|
||
... | ... | |
* It returns a string representation of the value and a string representation of the unit
|
||
* to let it be i18n-able
|
||
*/
|
||
def prettyMo(m:MemorySize) : (String,String) = {
|
||
private def prettyMo(m:MemorySize) : (String,String) = {
|
||
val x = prettyPrint(m, "B" :: "kB" :: "MB" :: "GB" :: "TB" :: "PB" :: "EB" :: "ZB" :: "YB" :: Nil)
|
||
(x._1.bigDecimal.stripTrailingZeros.toPlainString,x._2)
|
||
}
|
||
... | ... | |
val pres3 = new java.math.MathContext(3)
|
||
|
||
def round(m:BigDecimal) : BigDecimal = {
|
||
if(m < 1) throw new TechnicalException("Could not round number strictly smaller than one. Seems to be an algo error, check with the dev.")
|
||
if(m < 1) throw new IllegalArgumentException("Could not round number strictly smaller than one. Seems to be an algo error, check with the dev.")
|
||
else if(m < 1000) m.round(pres3)
|
||
else BigDecimal(m.toBigInt)
|
||
}
|
||
|
||
def rec(m:BigDecimal, u:List[String]) : (BigDecimal,String)= u match {
|
||
case Nil => throw new TechnicalException("At list one unit have to be provided for the memory pretty printer. Look around in the class where the dev don't use that method correctly.")
|
||
case Nil => throw new IllegalArgumentException("At list one unit have to be provided for the memory pretty printer. Look around in the class where the dev don't use that method correctly.")
|
||
case h::Nil => //no bigger units, stop here
|
||
(round(m),h)
|
||
case h::tail =>
|
inventory-provisioning-web/pom.xml | ||
---|---|---|
<version>${commons-io-version}</version>
|
||
</dependency>
|
||
<!-- Spring Framework Project libraries -->
|
||
<!-- Needed to use spring configuration by annotation -->
|
||
<dependency>
|
||
<groupId>com.normation</groupId>
|
||
<artifactId>spring-run-dependencies</artifactId>
|
||
<version>${spring-run-dep-version}</version>
|
||
<type>pom</type>
|
||
<groupId>cglib</groupId>
|
||
<artifactId>cglib</artifactId>
|
||
</dependency>
|
||
<!-- the slf4j commons-logging replacement -->
|
||
<dependency>
|
||
<groupId>org.slf4j</groupId>
|
||
<artifactId>jcl-over-slf4j</artifactId>
|
||
</dependency>
|
||
<dependency>
|
||
<groupId>org.springframework</groupId>
|
inventory-repository/pom.xml | ||
---|---|---|
<artifactId>utils</artifactId>
|
||
<version>${rudder-version}</version>
|
||
</dependency>
|
||
<dependency>
|
||
<groupId>com.normation</groupId>
|
||
<artifactId>historization-api</artifactId>
|
||
<version>${rudder-version}</version>
|
||
</dependency>
|
||
<dependency>
|
||
<groupId>com.normation</groupId>
|
||
<artifactId>scala-ldap</artifactId>
|
||
... | ... | |
<artifactId>lift-json_${scala-binary-version}</artifactId>
|
||
<version>${lift-version}</version>
|
||
</dependency>
|
||
<dependency>
|
||
<groupId>net.liftweb</groupId>
|
||
<artifactId>lift-util_${scala-binary-version}</artifactId>
|
||
<version>${lift-version}</version>
|
||
</dependency>
|
||
</dependencies>
|
||
|
||
</project>
|
inventory-repository/src/main/scala/com/normation/history/HistoryLog.scala | ||
---|---|---|
/*
|
||
*************************************************************************************
|
||
* Copyright 2011 Normation SAS
|
||
*************************************************************************************
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
*************************************************************************************
|
||
*/
|
||
|
||
package com.normation.history
|
||
|
||
import org.joda.time.DateTime
|
||
|
||
/**
|
||
* Represent history data.
|
||
* The actual data type is let to be defined.
|
||
*/
|
||
trait HistoryLog[ID,V, T] {
|
||
|
||
/**
|
||
* Id of the history log.
|
||
* One log has seceral version for
|
||
* only one ID (the couple (ID,version) is unique)
|
||
*/
|
||
def id:ID
|
||
|
||
/**
|
||
* Date and Time for which the data are saved
|
||
* @return
|
||
*/
|
||
def datetime : DateTime
|
||
|
||
/**
|
||
* History type, should be linked to T
|
||
*/
|
||
def historyType : String
|
||
|
||
/**
|
||
* Version of the history
|
||
* Version must be comparable, but du to
|
||
* Inconsistencies between libraries and APIs,
|
||
* we are not able to provide a type constrain.
|
||
* bigger are newer
|
||
* @return
|
||
*/
|
||
def version:V
|
||
|
||
/**
|
||
* Actual data saved in the history
|
||
* @return
|
||
*/
|
||
def data : T
|
||
|
||
}
|
inventory-repository/src/main/scala/com/normation/history/HistoryLogRepository.scala | ||
---|---|---|
/*
|
||
*************************************************************************************
|
||
* Copyright 2011 Normation SAS
|
||
*************************************************************************************
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
*************************************************************************************
|
||
*/
|
||
|
||
package com.normation.history
|
||
|
||
import net.liftweb.common.Box
|
||
import org.joda.time.DateTime
|
||
|
||
trait WriteOnlyHistoryLogRepository[ID, V, T, HLog <: HistoryLog[ID,V, T]] {
|
||
|
||
/**
|
||
* Save a report and return the ID of the saved report, and
|
||
* it's version
|
||
* @param historyLog
|
||
* @return
|
||
*/
|
||
def save(id:ID,data:T,datetime:DateTime = DateTime.now) : Box[HLog]
|
||
|
||
}
|
||
|
||
trait ReadOnlyHistoryLogRepository[ID, V, T, HLog <: HistoryLog[ID,V, T]] {
|
||
|
||
/**
|
||
* Retrieve all ids known by the repository
|
||
*/
|
||
def getIds : Box[Seq[ID]]
|
||
|
||
/**
|
||
* Get the list of record for the given ID
|
||
* @return
|
||
* Failure(message) if an error happened
|
||
* Empty if a not specified error happened
|
||
* Full(seq) if that id exists. Seq may be empty
|
||
* if no version are available.
|
||
*/
|
||
def getAll(id:ID) : Box[Seq[HLog]]
|
||
|
||
/**
|
||
* Get the last record for the given ID.
|
||
* @return
|
||
* Failure(message) or Empty if an error happened, or
|
||
* if the id does not exists or has no recorded history
|
||
* Full(hlog) the last recorded version of hlog
|
||
*/
|
||
def getLast(id:ID) : Box[HLog]
|
||
|
||
/**
|
||
* Get the last record for the given ID and version.
|
||
* @return
|
||
* Failure(message) or Empty if an error happened, or
|
||
* if the id does not exists or has no such version in
|
||
* recorded history
|
||
* Full(hlog) the recorded version of hlog
|
||
*/
|
||
def get(id:ID, version:V) : Box[HLog]
|
||
|
||
|
||
/**
|
||
* Return the list of version for ID.
|
||
* @return
|
||
* Failure(message) if an error happened
|
||
* Empty if a not specified error happened
|
||
* Full(seq) if that id exists. Seq may be empty
|
||
* if no version are available.
|
||
* Seq is sorted with last version (most recent) first
|
||
* and so ( versions.head > versions.head.head )
|
||
*/
|
||
def versions(id:ID) : Box[Seq[V]]
|
||
}
|
||
|
||
trait HistoryLogRepository[ID, V, T, HLog <: HistoryLog[ID,V, T]] extends
|
||
WriteOnlyHistoryLogRepository[ID, V, T, HLog] with
|
||
ReadOnlyHistoryLogRepository[ID, V, T, HLog] {}
|
inventory-repository/src/main/scala/com/normation/history/impl/FileHistoryLogRepository.scala | ||
---|---|---|
/*
|
||
*************************************************************************************
|
||
* Copyright 2011 Normation SAS
|
||
*************************************************************************************
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
*************************************************************************************
|
||
*/
|
||
|
||
package com.normation.history
|
||
package impl
|
||
|
||
import java.io.File
|
||
import org.joda.time.DateTime
|
||
import org.joda.time.format.DateTimeFormat
|
||
|
||
import net.liftweb.common._
|
||
import net.liftweb.util.ControlHelpers.tryo
|
||
import FileHistoryLogRepository._
|
||
|
||
/**
|
||
* A trait that allows to write and read datas of type T
|
||
* to/from files
|
||
*/
|
||
trait FileMarshalling[T] {
|
||
def fromFile(in:File) : Box[T]
|
||
def toFile(out:File, data: T) : Box[T]
|
||
}
|
||
|
||
trait IdToFilenameConverter[ID] {
|
||
/**
|
||
* Create the filename from the id, and vice versa
|
||
*/
|
||
def idToFilename(id:ID) : String
|
||
|
||
/**
|
||
* Get the ID from a path name
|
||
*/
|
||
def filenameToId(name:String) : ID
|
||
}
|
||
|
||
/**
|
||
* An implementation of HistoryLogRepository that save datas in
|
||
* FileSystem, using a layout like:
|
||
*
|
||
* /(d)root_dir/
|
||
* |- (d)history_id1
|
||
* | |- (f)datetime1
|
||
* | |- (f)datetime2
|
||
* | `...
|
||
* ` (d)history_name2
|
||
*
|
||
* Any datas type may be used, as long as they can be read/write from
|
||
* files
|
||
*/
|
||
class FileHistoryLogRepository[ID,T](
|
||
val rootDir:String,
|
||
val marshaller:FileMarshalling[T],
|
||
val converter:IdToFilenameConverter[ID]
|
||
) extends HistoryLogRepository[ID, DateTime, T, DefaultHLog[ID,T]] {
|
||
|
||
type HLog = DefaultHLog[ID,T]
|
||
|
||
//when the the class is instanciated, try to create the directory
|
||
//we don't want to catch exception here
|
||
root()
|
||
|
||
|
||
//we don't want to catch exception here
|
||
private def root() : Box[File] = {
|
||
for {
|
||
dir <- tryo(new File(rootDir))
|
||
isValid <- if(dir.exists && !dir.isDirectory) Failure(s"'${dir.getAbsolutePath}' exists and is not a directory") else Full("OK")
|
||
isCreated <- if(!dir.exists) tryo(dir.mkdirs) else Full("OK")
|
||
} yield {
|
||
dir
|
||
}
|
||
}
|
||
|
||
//we don't want to catch exception here
|
||
private def idDir(id:ID) = {
|
||
for {
|
||
r <- root
|
||
dir <- tryo(new File(r, converter.idToFilename(id)))
|
||
isValid <- if(dir.exists && !dir.isDirectory) Failure(s"'${dir.getAbsolutePath}' and is not a directory") else Full("OK")
|
||
isCreated <- if(!dir.exists) tryo(dir.mkdirs) else Full("OK")
|
||
} yield {
|
||
dir
|
||
}
|
||
}
|
||
|
||
//we don't want to catch exception here
|
||
private def exists(id:ID) = {
|
||
(for{
|
||
r <- root
|
||
dir <- tryo(new File(r, converter.idToFilename(id)))
|
||
} yield dir.exists && dir.isDirectory).getOrElse(false)
|
||
}
|
||
|
||
/**
|
||
* Save a report and return the ID of the saved report, and
|
||
* it's version
|
||
* @param historyLog
|
||
* @return
|
||
*/
|
||
def save(id:ID,data:T,datetime:DateTime = DateTime.now) : Box[HLog] = {
|
||
converter.idToFilename(id) match {
|
||
case null | "" =>
|
||
Failure("History log name can not be null nor empty")
|
||
case s if(s.contains(System.getProperty("file.separator"))) =>
|
||
Failure(s"UUID can not contains the char '${System.getProperty("file.separator")}'")
|
||
case s =>
|
||
val hlog = DefaultHLog(id,datetime,data)
|
||
|
||
for {
|
||
i <- idDir(hlog.id)
|
||
file <- tryo(new File(i,vToS(hlog.version)))
|
||
datas <- tryo(marshaller.toFile(file,hlog.data))
|
||
} yield hlog
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Retrieve all ids known by the repository
|
||
*/
|
||
def getIds : Box[Seq[ID]] = {
|
||
for {
|
||
r <- root()
|
||
res <- tryo(r.listFiles.collect { case(f) if(f.isDirectory) => converter.filenameToId(f.getName) })
|
||
} yield {
|
||
res
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Get the list of all history logs for the given id.
|
||
* If reading any logs throws an error, the full result is a
|
||
* Failure
|
||
*/
|
||
def getAll(id:ID) : Box[Seq[HLog]] = {
|
||
for {
|
||
versions <- this.versions(id)
|
||
hlogs <- {
|
||
( (Full(Seq()):Box[Seq[HLog]]) /: versions ) { (current, v) =>
|
||
for {
|
||
seq <- current
|
||
hlog <- this.get(id,v)
|
||
} yield seq :+ hlog
|
||
}
|
||
}
|
||
} yield hlogs
|
||
}
|
||
|
||
/**
|
||
* Get the list of record for the given UUID and version.
|
||
* If no version is specified, get the last.
|
||
*/
|
||
def getLast(id:ID) : Box[HLog] = {
|
||
for {
|
||
versions <- this.versions(id)
|
||
version <- versions.headOption
|
||
hlog <- this.get(id,version)
|
||
} yield hlog
|
||
}
|
||
|
||
/**
|
||
* Get the list of record for the given UUID and version.
|
||
* If no version is specified, get the last.
|
||
*/
|
||
def get(id:ID, version:DateTime) : Box[HLog] = {
|
||
for {
|
||
i <- idDir(id)
|
||
file <- tryo(new File(i,vToS(version)))
|
||
data <- marshaller.fromFile(file)
|
||
}yield DefaultHLog(id,version,data)
|
||
}
|
||
|
||
|
||
/**
|
||
* Return the list of version for ID.
|
||
* Full(Empty list) or Empty if no version for the given id
|
||
* List is sorted with last version (most recent) first
|
||
* ( versions.head > versions.head.head )
|
||
*/
|
||
def versions(id:ID) : Box[Seq[DateTime]] = {
|
||
for {
|
||
i <- idDir(id)
|
||
if(exists(id))
|
||
res <- tryo {
|
||
i.listFiles.toSeq.map(f => sToV(f.getName)).
|
||
filter(_.isDefined).map(_.get).sortWith(_ .compareTo(_) > 0)
|
||
}
|
||
} yield res
|
||
}
|
||
|
||
}
|
||
|
||
object FileHistoryLogRepository {
|
||
private val formatter = DateTimeFormat.forPattern("YYYY-MM-dd_HH:mm.ss.SSS")
|
||
private def vToS(version:DateTime) = formatter.print(version)
|
||
private def sToV(version:String):Option[DateTime] = {
|
||
try {
|
||
Some(formatter.parseDateTime(version))
|
||
} catch {
|
||
case e:IllegalArgumentException => None
|
||
}
|
||
}
|
||
}
|
inventory-repository/src/main/scala/com/normation/history/impl/HistoryLogImpl.scala | ||
---|---|---|
/*
|
||
*************************************************************************************
|
||
* Copyright 2011 Normation SAS
|
||
*************************************************************************************
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
*************************************************************************************
|
||
*/
|
||
|
||
package com.normation.history
|
||
package impl
|
||
|
||
import org.joda.time.DateTime
|
||
import com.normation.utils.HashcodeCaching
|
||
|
||
/**
|
||
* A version of history log which uses DateTime as
|
||
* version number.
|
||
*
|
||
*/
|
||
trait DatedHistoryLog[ID,T] extends HistoryLog[ID, DateTime, T] {
|
||
|
||
def datetime = version
|
||
|
||
}
|
||
|
||
|
||
case class DefaultHLog[ID,T](
|
||
id : ID,
|
||
version: DateTime,
|
||
data : T
|
||
) extends DatedHistoryLog[ID,T] with HashcodeCaching {
|
||
override val datetime = version
|
||
override val historyType = "default"
|
||
}
|
||
|
inventory-repository/src/main/scala/com/normation/inventory/ldap/core/InventoryDit.scala | ||
---|---|---|
|
||
|
||
import LDAPConstants._
|
||
import com.normation.utils.Utils.nonEmpty
|
||
import com.normation.inventory.domain._
|
||
import net.liftweb.common._
|
||
import com.normation.utils.HashcodeCaching
|
inventory-repository/src/test/scala/com/normation/history/impl/TestFileHistoryLogRepository.scala | ||
---|---|---|
/*
|
||
*************************************************************************************
|
||
* Copyright 2011 Normation SAS
|
||
*************************************************************************************
|
||
*
|
||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
* you may not use this file except in compliance with the License.
|
||
* You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* Unless required by applicable law or agreed to in writing, software
|
||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
* See the License for the specific language governing permissions and
|
||
* limitations under the License.
|
||
*
|
||
*************************************************************************************
|
||
*/
|
||
|
||
package com.normation.history.impl
|
||
|
||
import junit.framework.TestSuite
|
||
import org.junit._
|
||
import org.junit.Assert._
|
||
import org.junit.runner.RunWith
|
||
import org.junit.runners.BlockJUnit4ClassRunner
|
||
|
||
import org.joda.time.DateTime
|
||
import org.apache.commons.io.FileUtils
|
||
import java.io.File
|
||
|
||
import net.liftweb.util.ControlHelpers.tryo
|
||
|
||
import net.liftweb.common._
|
||
|
||
object StringMarshaller extends FileMarshalling[String] {
|
||
//simply read / write file content
|
||
override def fromFile(in:File) : Box[String] = tryo(FileUtils.readFileToString(in,"UTF-8"))
|
||
override def toFile(out:File, data: String) : Box[String] = tryo {
|
||
FileUtils.writeStringToFile(out,data, "UTF-8")
|
||
data
|
||
}
|
||
}
|
||
|
||
object StringId extends IdToFilenameConverter[String] {
|
||
override def idToFilename(id:String) : String = id
|
||
override def filenameToId(name:String) : String = name
|
||
}
|
||
|
||
import TestFileHistoryLogRepository._
|
||
|
||
@RunWith(classOf[BlockJUnit4ClassRunner])
|
||
class TestFileHistoryLogRepository {
|
||
|
||
val repos = new FileHistoryLogRepository(rootDir, StringMarshaller,StringId)
|
||
|
||
@Test def basicTest {
|
||
val id1 = "data1"
|
||
assertEquals(Full(List()), repos.getIds.map(_.toList))
|
||
assertEquals( _:EmptyBox, repos.versions(id1))
|
||
|
||
val data1 = "Some data 1\nwith multiple lines"
|
||
|
||
//save first revision
|
||
val data1time1 = DateTime.now()
|
||
assertEquals(Full(DefaultHLog(id1, data1time1,data1)), repos.save(id1, data1, data1time1))
|
||
|
||
//now we have exaclty one id, with one revision, equals to data1time1
|
||
assertEquals(Full(List(id1)), repos.getIds.map(_.toList))
|
||
assertEquals(Full(List(data1time1)), repos.versions(id1).map(_.toList))
|
||
|
||
//save second revision
|
||
val data1time2 = DateTime.now()
|
||
assertEquals(Full(DefaultHLog(id1, data1time2, data1)), repos.save(id1, data1, data1time2))
|
||
|
||
//now we have exaclty one id1, with two revisions, and head is data1time2
|
||
assertEquals(Full(List(id1)), repos.getIds.map(_.toList))
|
||
assertEquals(Full(data1time2 :: data1time1 :: Nil), repos.versions(id1).map(_.toList))
|
||
|
||
}
|
||
}
|
||
|
||
object TestFileHistoryLogRepository {
|
||
val rootDir = System.getProperty("java.io.tmpdir") + "/testFileHistoryLogRepo"
|
||
|
||
def clean {
|
||
FileUtils.deleteDirectory(new File(rootDir))
|
||
}
|
||
|
||
@BeforeClass def before = clean
|
||
@AfterClass def after = clean
|
||
|
||
}
|
Also available in: Unified diff
Fixes #11640: Autumn cleaning before 4.3 (ldap-inventory)