import gr.grnet.aquarium.{AquariumInternalError, AquariumException}
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
import gr.grnet.aquarium.logic.accounting.algorithm.SimpleExecutableChargingBehaviorAlgorithm
-import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLUtils}
-import gr.grnet.aquarium.policy.{EffectivePriceTable, PolicyModel, ResourceType, UserAgreementModel}
+import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot}
+import gr.grnet.aquarium.policy._
+import collection.immutable
+import com.ckkloverdos.maybe.Just
+import gr.grnet.aquarium.computation.Chargeslot
+import scala.Some
+import com.ckkloverdos.maybe.Just
+import gr.grnet.aquarium.computation.Chargeslot
+import scala.Some
+import com.ckkloverdos.maybe.Just
+import gr.grnet.aquarium.policy.ResourceType
+import gr.grnet.aquarium.policy.EffectiveUnitPrice
+import gr.grnet.aquarium.computation.Chargeslot
+import scala.Some
/**
* Methods for converting accounting events to wallet entries.
*/
trait TimeslotComputations extends Loggable {
// TODO: favour composition over inheritance until we decide what to do with DSLUtils (and TimeslotComputations).
- protected val dslUtils = new DSLUtils {}
+ //protected val dslUtils = new DSLUtils {}
/**
* Breaks a reference timeslot (e.g. billing period) according to policies and agreements.
val clog = ContextualLogger.fromOther(clogOpt, logger, "resolveEffectiveUnitPrices()")
// Note that most of the code is taken from calcChangeChunks()
- dslUtils.resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType)
+ val ret = resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType)
+ ret map {case (t,p) => (t,p.unitPrice)}
}
protected
a.head :: alignTimeslots(safeTail(a), safeTail(b))
}
}
+
+ type PriceMap = immutable.SortedMap[Timeslot, EffectiveUnitPrice]
+ type PriceList = List[EffectiveUnitPrice]
+ private def emptyMap = immutable.SortedMap[Timeslot,EffectiveUnitPrice]()
+
+ /**
+ * Resolves the effective price list for each chunk of the
+ * provided timeslot and returns it as a Map
+ */
+ private def resolveEffectiveUnitPricesForTimeslot(
+ alignedTimeslot: Timeslot,
+ policy: PolicyModel,
+ agreement: UserAgreementModel,
+ resourceType: ResourceType
+ ): PriceMap = {
+
+ val role = agreement.role
+ val fullPriceTable = agreement.fullPriceTableRef match {
+ case PolicyDefinedFullPriceTableRef ⇒
+ policy.roleMapping.get(role) match {
+ case Some(fullPriceTable) ⇒
+ fullPriceTable
+
+ case None ⇒
+ throw new AquariumInternalError("Unknown role %s".format(role))
+ }
+
+ case AdHocFullPriceTableRef(fullPriceTable) ⇒
+ fullPriceTable
+ }
+
+ val effectivePriceTable = fullPriceTable.perResource.get(resourceType.name) match {
+ case None ⇒
+ throw new AquariumInternalError("Unknown resource type %s".format(role))
+
+ case Some(effectivePriceTable) ⇒
+ effectivePriceTable
+ }
+
+ resolveEffective(alignedTimeslot, effectivePriceTable.priceOverrides)
+ }
+
+ private def printPriceList(p: PriceList) : Unit = {
+ Console.err.println("BEGIN PRICE LIST")
+ for { p1 <- p } Console.err.println(p1)
+ Console.err.println("END PRICE LIST")
+ }
+
+ private def printPriceMap(m: PriceMap) = {
+ Console.err.println("BEGIN PRICE MAP")
+ for { (t,p) <- m.toList } Console.err.println("Timeslot " + t + "\t\t" + p)
+ Console.err.println("END PRICE MAP")
+ }
+
+ private def resolveEffective(alignedTimeslot: Timeslot,p:PriceList): PriceMap = {
+ Console.err.println("\n\nInput timeslot: " + alignedTimeslot + "\n\n")
+ printPriceList(p)
+ val ret = resolveEffective3(alignedTimeslot,p) //HERE
+ printPriceMap(ret)
+ ret
+ }
+
+
+ private def resolveEffective3(alignedTimeslot: Timeslot, effectiveUnitPrices: PriceList): PriceMap =
+ effectiveUnitPrices match {
+ case Nil =>
+ emptyMap
+ case hd::tl =>
+ val (satisfied,notSatisfied) = hd splitTimeslot alignedTimeslot
+ val satisfiedMap = satisfied.foldLeft (emptyMap) {(map,t) =>
+ //Console.err.println("Adding timeslot" + t +
+ // " for policy " + policy.name)
+ map + ((t,hd))
+ }
+ val notSatisfiedMap = notSatisfied.foldLeft (emptyMap) {(map,t) =>
+ val otherMap = resolveEffective3(t,tl)
+ //Console.err.println("Residual timeslot: " + t)
+ val ret = map ++ otherMap
+ ret
+ }
+ val ret = satisfiedMap ++ notSatisfiedMap
+ ret
+ }
}
*/
trait DSLUtils {
- /**
- * Resolves the effective price list for each chunk of the
- * provided timeslot and returns it as a Map
- */
- def resolveEffectiveUnitPricesForTimeslot(
- alignedTimeslot: Timeslot,
- policy: PolicyModel,
- agreement: UserAgreementModel,
- resourceType: ResourceType
- ): immutable.SortedMap[Timeslot, Double] = {
- val role = agreement.role
- val fullPriceTable = agreement.fullPriceTableRef match {
- case PolicyDefinedFullPriceTableRef ⇒
- policy.roleMapping.get(role) match {
- case Some(fullPriceTable) ⇒
- fullPriceTable
+ //TODO pgerakios: REMOVE THIS FILE
- case None ⇒
- throw new AquariumInternalError("Unknown role %s".format(role))
- }
-
- case AdHocFullPriceTableRef(fullPriceTable) ⇒
- fullPriceTable
- }
-
- val effectivePriceTable = fullPriceTable.perResource.get(resourceType.name) match {
- case None ⇒
- throw new AquariumInternalError("Unknown resource type %s".format(role))
-
- case Some(effectivePriceTable) ⇒
- effectivePriceTable
- }
-
- resolveEffective(alignedTimeslot, effectivePriceTable)
- }
-
- /*private def printPolicy[T <: DSLTimeBoundedItem[T]](t : T) : Unit = {
- Console.err.println("Policy " + t.name + " " + t.toTimeslot + " DETAIL : " + t.effective)
- t.overrides match {
- case None => Console.println
- case Some(t) => printPolicy(t)
- }
- }
-
- private def printMap[T <: DSLTimeBoundedItem[T]](m: immutable.SortedMap[Timeslot, T]) = {
- Console.err.println("BEGIN MAP: ")
- for { (t,p) <- m.toList } Console.err.println("Timeslot " + t + "\t\t" + p.name)
- Console.err.println("END MAP")
- } */
-
- def resolveEffective[T <: DSLTimeBoundedItem[T]](timeslot0: Timeslot,policy: T):
- immutable.SortedMap[Timeslot, T] = {
- //Console.err.println("\n\nInput timeslot: " + timeslot0 + "\n\n")
- ///printPolicy(policy)
- val ret = resolveEffective3(timeslot0,policy) //HERE
- //printMap(ret)
- ret
- }
-
-
- def resolveEffective3(
- alignedTimeslot: Timeslot,
- effectivePriceTable: EffectivePriceTable
- ): immutable.SortedMap[Timeslot, Double/*unit price*/] = {
-// assert(policy.toTimeslot contains timeslot0,"Policy does not contain timeslot")
- val timeslot = alignedTimeslot //TODO: timeslot0.align(5000)
- val subtimeslots_of_this_policy = Timeslot.mergeOverlaps(policy.effective intervalsOf timeslot)
- val subtimeslots_NOT_IN_this_policy = Timeslot.mergeOverlaps(timeslot.nonOverlappingTimeslots
- (subtimeslots_of_this_policy))
- val policy_map = subtimeslots_of_this_policy.foldLeft (immutable.SortedMap[Timeslot, T]())
- {(map,t) =>
- //Console.err.println("Adding timeslot" + t + " for policy " + policy.name)
- map + ((t,policy))
- }
- val other_policy_map = policy.overrides match {
- case None =>
- immutable.SortedMap[Timeslot, T]()
- case Some(parent_policy)=>
- subtimeslots_NOT_IN_this_policy.foldLeft (
- (immutable.SortedMap[Timeslot, T]()))
- {(map,t) =>
- //Console.err.println("Residual timeslot: " + t)
- map ++ resolveEffective3(t,parent_policy)
- }
- }
- val final_map = policy_map ++ other_policy_map
- final_map
- }
-
- /*def resolveEffective2[T <: DSLTimeBoundedItem[T]](timeslot0: Timeslot,policy: T):
- immutable.SortedMap[Timeslot, T] = {
- assert(policy.toTimeslot contains timeslot0,"Policy does not contain timeslot")
-
- /* generate mappings from timeslots -> policies
- * Algorithm: find next valid date (starting from timeslot.start) in this policy
- */
- val timeslot = timeslot0 //TODO: timeslot0.align(5000)
- def nextDate(d:Date,p:T) : Option[(Date,T,Boolean)] =
- (p.effective nextValidAfter d,p.overrides) match {
- case (None,None) => None
- case (None,Some(parent_policy)) =>
- val d1 = nextDate(d,parent_policy)
- d1
- case (Some(d1),_) => /* the next valid date cannot occur after the end of timeslot*/
- if (d1.before(timeslot.to)) Some((d1,p,true)) else Some((timeslot.to,p,false))
- }
- def genMap(map: immutable.SortedMap[Timeslot, T],d:Date) : immutable.SortedMap[Timeslot, T] = {
- val step = 1000L
- nextDate(d,policy) match {
- case None => map
- case Some((d1,policy,cont)) =>
- val t = Timeslot(d,d1)
- val map1 = map + (t -> policy)
- if(cont) genMap(map1,new Date(d1.getTime + step)) // 1 second after d1
- else map1 /* done */
- }
- }
- val map = genMap(immutable.SortedMap[Timeslot, T](),timeslot.from)
- map
- }
-
- /**
- * Splits the provided timeslot into chunks according to the validity
- * timeslots specified by the provided time bounded item. It
- * returns a map whose keys are the timeslot chunks and the values
- * correspond to the effective time bounded item (algorithm or pricelist).
- */
- def resolveEffective1[T <: DSLTimeBoundedItem[T]](timeslot: Timeslot,policy: T):
- immutable.SortedMap[Timeslot, T] = {
- assert(policy.toTimeslot contains timeslot,"Policy does not contain timeslot")
-
- /* Get a list of all effective/expanded policy timeslots within
- a timeslot specified by "variable timeslot".
- * NOTICE: The returned timeslots may be slightly out of "timeslot" bounds
- * so we need to invoke overlappingTimeslots and nonOverlapping timeslots
- */
- val all_timeslots = allEffectiveTimeslots(policy.effective,timeslot)
- val timeslots_IN_policy = timeslot.overlappingTimeslots(all_timeslots)
- val timeslots_NOT_IN_policy = timeslot.nonOverlappingTimeslots(all_timeslots)
-
- val ret = immutable.SortedMap[Timeslot, T]() ++
- /*add [timeslots -> policy] covered by this policy*/
- timeslots_IN_policy.flatMap {
- effective_timeslot => Map(effective_timeslot -> policy)
- } ++
- /*add [timeslots -> policy] covered by parent policies */
- timeslots_NOT_IN_policy.flatMap { /* search the policy hierarchy for effective timeslots not covered by this policy.*/
- not_effective_timeslot => policy.overrides match {
- case None => immutable.SortedMap[Timeslot, T]() /*Nothing to do. TODO: throw exception ?*/
- case Some(parent_policy) => resolveEffective1(not_effective_timeslot,parent_policy) /* search the policy hierarchy*/
- }
- }
- ret
- }*/
-
- /*
- /*
- * Get a list of all timeslots within which a timeframe
- * is effective, whithin the provided time bounds.
- */
- def allEffectiveTimeslots(spec: DSLTimeFrame, t: Timeslot):
- List[Timeslot] =
- if (spec.repeat.isEmpty) { //A simple timeframe with no repetition defined
- val fromDate = if (spec.from.before(t.from)) t.from else spec.from
- val toDate = if (spec.to.getOrElse(t.to).after(t.to)) t.to else spec.to.getOrElse(t.to)
- List(Timeslot(fromDate, toDate))
- } /* otherwise for all repetitions determine timeslots*/
- else {
- val all = for { r <- spec.repeat
- ts <- effectiveTimeslots(r, t.from, t.to)
- } yield ts
- //for{ i <- all} Console.err.println(i)
- mergeOverlaps(all)
- }
- */
-
-
-
- /**
- * Merges overlapping timeslots.
- */
-
- /*/**
- * Get a list of all timeslots within which a time frame is active.
- The result is returned sorted by timeframe start date.
- */
- def effectiveTimeslots(spec: DSLTimeFrameRepeat, from: Date, to : Date):
- List[Timeslot] =
- for { (h1,h2) <- spec.start zip spec.end
- (d1,d2) <- DSLTimeSpec.expandTimeSpec(h1,h2,from,to)
- }
- yield Timeslot(d1,d2)
- */
}
--- /dev/null
+/*
+ * Copyright 2011-2012 GRNET S.A. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and
+ * documentation are those of the authors and should not be
+ * interpreted as representing official policies, either expressed
+ * or implied, of GRNET S.A.
+ */
+package gr.grnet.aquarium.policy
+
+import org.quartz.CronExpression
+import java.util.Date
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
+
+/**
+ * @author Prodromos Gerakios <pgerakios@grnet.gr>
+ */
+
+
+case class CronSpec(cronSpec: String) {
+
+ private val cronExpr = {
+ val e = "00 " + cronSpec.trim //IMPORTANT: WE DO NOT CARE ABOUT SECONDS!!!
+ val l = e.split(" ")
+ assert(l.size == 6,"Invalid cron specification")
+ //for {ll <- l } Console.err.println(ll)
+ (l(3),l(5)) match {
+ case ("?",_) | (_,"?") => ()
+ case (_,"*") => l.update(5,"?")
+ case ("*",_) => l.update(3,"?")
+ }
+ val e1 = l.foldLeft("") { (s,elt) => s + " " + elt}
+ //Console.err.println("e = " + e + " and e1 = " + e1)
+ new CronExpression(e1)
+ }
+
+ def includes(d:Date) : Boolean =
+ cronExpr isSatisfiedBy d
+
+ /* the next valid date cannot outlive (min,max)*/
+ def nextValidDate(t:Timeslot,d:Date) : Option[Date] =
+ (cronExpr getNextValidTimeAfter d) match {
+ case null =>
+ None
+ case d1 =>
+ val (min0,max0) = (t.from,t.to)
+ val (min,max,e) = (min0.getTime,max0.getTime,d1.getTime)
+ if(e < min || e>max)
+ None
+ else
+ Some({assert(d1.getTime>=d.getTime);d1})
+ }
+
+ override def toString : String = cronSpec
+}
\ No newline at end of file
package gr.grnet.aquarium.policy
-import gr.grnet.aquarium.logic.accounting.dsl.DSLTimeFrame
+import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLCronSpec, DSLTimeFrame}
+import collection.mutable
/**
*
* @author Christos KK Loverdos <loverdos@gmail.com>
+ * @author Prodromos Gerakios <pgerakios@grnet.gr>
*/
-case class EffectiveUnitPrice(unitPrice: Double, when: DSLTimeFrame) // TODO: use DSLTimeframe
+case class EffectiveUnitPrice(unitPrice: Double, when: Option[(CronSpec,CronSpec)]) { // TODO: use DSLTimeframe
+
+ /* Split a timeslot T into two *sets* S and S2 consisting of timeslots such that
+ * (a) each element in S1,S2 is contained in T
+ * (b) for all x in S1 and y in S2 there is no overlap between x and y.
+ * (c) the union of all x in S1 and y S2 is T
+ * (d) the elements of S1 satisfy the cron spec ``when''
+ * (e) the elements of S2 do NOT satisfy the cron spec ``when''
+ */
+ def splitTimeslot(t:Timeslot) : (List[Timeslot],List[Timeslot])=
+ when match {
+ case None =>
+ (List(t),Nil)
+ case Some((start,end)) =>
+ val result = new mutable.ListBuffer[Timeslot]()
+ var offset = t.from
+ while(start.nextValidDate(t,offset) match {
+ case None =>
+ false
+ case Some(d_start) =>
+ end.nextValidDate(t,d_start) match {
+ case None =>
+ result += Timeslot(d_start,t.to)
+ false
+ case Some(d_end) =>
+ result += Timeslot(d_start,d_end)
+ offset = d_end
+ d_end.before(t.to)
+ }
+ }) ()
+ val l = result.toList
+ val l1 = Timeslot mergeOverlaps l
+ val l2 = t nonOverlappingTimeslots l1
+ val l3 = Timeslot mergeOverlaps l2
+ (l1,l3)
+ }
+
+ private def stringOfStartCron = when match {
+ case None => "? ? ? ? ?"
+ case Some((s,_)) => s.toString
+ }
+ private def stringOfEndCron = when match {
+ case None => "? ? ? ? ?"
+ case Some((_,s)) => s.toString
+ }
+ override def toString : String = "EffectiveUnitPrice(%d,%s,%s)".
+ format(unitPrice,stringOfStartCron,stringOfEndCron)
+ }
package gr.grnet.aquarium.logic.test
import org.junit.Test
-import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLTimeFrameRepeat, DSLTimeSpec, DSLTimeFrame}
+import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLTimeFrameRepeat,DSLTimeFrame}
import gr.grnet.aquarium.util.TestMethods
import java.util.Date
import scala._
* Represents an effectivity timeframe.
*
* @author Prodromos Gerakios <pgerakios@grnet.gr>
-*/
+/**/
class DSLTimeFrameTest extends DSLTestBase with TestMethods {
private def makeRepeat(startCron:String,endCron:String): DSLTimeFrameRepeat={
//TODO: add timeslot contained in big timeslot
}
-}
\ No newline at end of file
+} */
\ No newline at end of file