<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
- <version>3.0.1</version>
+ <version>3.1.0</version>
<configuration>
+ <recompileMode>incremental</recompileMode>
+
<charset>${project.build.sourceEncoding}</charset>
<jvmArgs>
<jvmArg>-Xmx1024m</jvmArg>
-{
- "id":"750E6309-AB60-41B4-8D4B-9FFEA6EF843C",
+{ "jsonClass":"gr.grnet.aquarium.policy.StdPolicy",
+
+ "id":"default-policy",
"validityTimespan":{
"fromMillis":0,
"toMillis":9223372036854775807
},
- "resourceTypes":[
- {
- "name":"addcredits",
- "unit":"credits",
- "chargingBehavior":"gr.grnet.aquarium.charging.OnceChargingBehavior"
- },
- {
- "name":"bandwidth",
- "unit":"MB/Hr",
- "chargingBehavior":"gr.grnet.aquarium.charging.DiscreteChargingBehavior"
- },
- {
- "name":"vmtime",
- "unit":"Hr",
- "chargingBehavior":"gr.grnet.aquarium.charging.OnOffChargingBehavior"
- },
- {
- "name":"diskspace",
- "unit":"MB/Hr",
- "chargingBehavior":"gr.grnet.aquarium.charging.ContinuousChargingBehavior"
- }
- ],
+ "resourceTypes":[{
+ "jsonClass":"gr.grnet.aquarium.policy.ResourceType",
+ "name":"diskspace",
+ "unit":"MB/Hr",
+ "chargingBehavior":"gr.grnet.aquarium.charging.ContinuousChargingBehavior"
+ },{
+ "jsonClass":"gr.grnet.aquarium.policy.ResourceType",
+ "name":"vmtime",
+ "unit":"Hr",
+ "chargingBehavior":"gr.grnet.aquarium.charging.VMChargingBehavior"
+ },{
+ "jsonClass":"gr.grnet.aquarium.policy.ResourceType",
+ "name":"addcredits",
+ "unit":"credits",
+ "chargingBehavior":"gr.grnet.aquarium.charging.OnceChargingBehavior"
+ }],
"chargingBehaviors":[
- "gr.grnet.aquarium.charging.DiscreteChargingBehavior",
- "gr.grnet.aquarium.charging.OnOffChargingBehavior",
+ "gr.grnet.aquarium.charging.VMChargingBehavior",
"gr.grnet.aquarium.charging.ContinuousChargingBehavior",
"gr.grnet.aquarium.charging.OnceChargingBehavior"
],
"roleMapping":{
"default":{
+ "jsonClass":"gr.grnet.aquarium.policy.FullPriceTable",
+
"perResource":{
- "bandwidth":{
- "priceOverrides":[
- {
+ "diskspace":{
+ "default":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+ "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
"unitPrice":0.01
- }
- ]
+ }]
+ }
},
+
"vmtime":{
- "priceOverrides":[
- {
+ "powerOn":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+ "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
"unitPrice":0.01
- }
- ]
+ }]
+ },
+ "powerOff":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+ "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
+ "unitPrice":0.0010
+ }]
+ }
},
- "diskspace":{
- "priceOverrides":[
- {
- "unitPrice":0.01
- }
- ]
- }
+
"addcredits":{
- "priceOverrides":[
- {
- "unitPrice":1
- }
- ]
+ "default":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+ "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
+ "unitPrice":-1.0
+ }]
+ }
}
}
}
-import gr.grnet.aquarium.charging.{OnceChargingBehavior, ContinuousChargingBehavior, OnOffChargingBehavior, DiscreteChargingBehavior}
+import gr.grnet.aquarium.charging.VMChargingBehavior.Selectors.Power
+import gr.grnet.aquarium.charging.{OnceChargingBehavior, ContinuousChargingBehavior, VMChargingBehavior}
+import gr.grnet.aquarium.policy.FullPriceTable._
import gr.grnet.aquarium.policy.{EffectiveUnitPrice, EffectivePriceTable, FullPriceTable, ResourceType, StdPolicy}
import gr.grnet.aquarium.Timespan
+import gr.grnet.aquarium.util.nameOfClass
// Definition of our standard policy in plain Scala
validityTimespan = Timespan(0),
resourceTypes = Set(
- ResourceType("bandwidth", "MB/Hr", classOf[DiscreteChargingBehavior].getName),
- ResourceType("vmtime", "Hr", classOf[OnOffChargingBehavior].getName),
- ResourceType("diskspace", "MB/Hr", classOf[ContinuousChargingBehavior].getName)
+ ResourceType("vmtime", "Hr", nameOfClass[VMChargingBehavior]),
+ ResourceType("diskspace", "MB/Hr", nameOfClass[ContinuousChargingBehavior])
),
chargingBehaviors = Set(
- classOf[DiscreteChargingBehavior].getName,
- classOf[OnOffChargingBehavior].getName,
- classOf[ContinuousChargingBehavior].getName,
- classOf[OnceChargingBehavior].getName
+ nameOfClass[VMChargingBehavior],
+ nameOfClass[ContinuousChargingBehavior],
+ nameOfClass[OnceChargingBehavior]
),
roleMapping = Map(
"default" -> FullPriceTable(Map(
- "bandwidth" -> EffectivePriceTable(EffectiveUnitPrice(0.01, None) :: Nil),
- "vmtime" -> EffectivePriceTable(EffectiveUnitPrice(0.01, None) :: Nil),
- "diskspace" -> EffectivePriceTable(EffectiveUnitPrice(0.01, None) :: Nil)
+ "diskspace" -> Map(
+ DefaultSelectorKey -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil)
+ ),
+ "vmtime" -> Map(
+ Power.powerOn -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil),
+ Power.powerOff -> EffectivePriceTable(EffectiveUnitPrice(0.001) :: Nil) // cheaper when the VM is OFF
+ )
))
)
)
\ No newline at end of file
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
import gr.grnet.aquarium.{Aquarium, AquariumInternalError, AquariumException}
-import gr.grnet.aquarium.policy.{UserAgreementModel, ResourceType}
+import gr.grnet.aquarium.policy.{FullPriceTable, EffectivePriceTable, UserAgreementModel, ResourceType}
import com.ckkloverdos.key.{TypedKey, TypedKeySkeleton}
import gr.grnet.aquarium.util._
import gr.grnet.aquarium.util.date.TimeHelpers
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-abstract class ChargingBehavior(val alias: String, val inputs: Set[ChargingInput]) extends Loggable {
+abstract class ChargingBehavior(
+ final val alias: String,
+ final val inputs: Set[ChargingInput],
+ final val selectorHierarchy: List[List[String]] = Nil) extends Loggable {
- final lazy val inputNames = inputs.map(_.name)
+ final val inputNames = inputs.map(_.name)
@inline private[this] def hrs(millis: Double) = {
val hours = millis / 1000 / 60 / 60
(credits, explanation)
- case ChargingBehaviorAliases.onoff ⇒
+ case ChargingBehaviorAliases.vmtime ⇒
val credits = hrs(timeDeltaMillis) * unitPrice
val explanation = "Time(%s) * Unit(%s)".format(hrs(timeDeltaMillis), unitPrice)
rcEvent.toDebugString
}
+ protected def computeSelectorPath(
+ chargingData: mutable.Map[String, Any],
+ currentResourceEvent: ResourceEventModel,
+ referenceTimeslot: Timeslot,
+ previousValue: Double,
+ totalCredits: Double,
+ oldAccumulatingAmount: Double,
+ newAccumulatingAmount: Double
+ ): List[String]
+
/**
*
* @param chargingData
referenceTimeslot.to.getTime
)
+ val effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable = fullPriceTable ⇒ {
+ this.selectEffectivePriceTable(
+ fullPriceTable,
+ chargingData,
+ currentResourceEvent,
+ referenceTimeslot,
+ previousValue,
+ totalCredits,
+ _oldAccumulatingAmount,
+ _newAccumulatingAmount
+ )
+ }
+
val initialChargeslots = TimeslotComputations.computeInitialChargeslots(
referenceTimeslot,
resourceType,
policyByTimeslot,
- agreementByTimeslot
+ agreementByTimeslot,
+ effectivePriceTableSelector
)
val fullChargeslots = initialChargeslots.map {
chargingData(envKey.name) = value
}
+ def selectEffectivePriceTable(
+ fullPriceTable: FullPriceTable,
+ chargingData: mutable.Map[String, Any],
+ currentResourceEvent: ResourceEventModel,
+ referenceTimeslot: Timeslot,
+ previousValue: Double,
+ totalCredits: Double,
+ oldAccumulatingAmount: Double,
+ newAccumulatingAmount: Double
+ ): EffectivePriceTable = {
+
+ val selectorPath = computeSelectorPath(
+ chargingData,
+ currentResourceEvent,
+ referenceTimeslot,
+ previousValue,
+ totalCredits,
+ oldAccumulatingAmount,
+ newAccumulatingAmount
+ )
+
+ fullPriceTable.effectivePriceTableOfSelectorForResource(selectorPath, currentResourceEvent.safeResource)
+ }
+
/**
*
* @param aquarium
* Typically all events are billable by default and indeed this is the default implementation
* provided here.
*
- * The only exception to the rule is ON events for [[gr.grnet.aquarium.charging.OnOffChargingBehavior]].
+ * The only exception to the rule is ON events for [[gr.grnet.aquarium.charging.VMChargingBehavior]].
*/
def isBillableEvent(event: ResourceEventModel): Boolean = true
* There are resources (cost policies) for which implicit events must be generated at the end of the billing period
* and also at the beginning of the next one. For these cases, this method must return `true`.
*
- * The motivating example comes from the [[gr.grnet.aquarium.charging.OnOffChargingBehavior]] for which we
+ * The motivating example comes from the [[gr.grnet.aquarium.charging.VMChargingBehavior]] for which we
* must implicitly assume `OFF` events at the end of the billing period and `ON` events at the beginning of the next
* one.
*
*/
object ChargingBehaviorAliases {
- final val onoff = "onoff"
+ final val vmtime = "vmtime"
final val discrete = "discrete"
final val continuous = "continuous"
final val once = "once"
package gr.grnet.aquarium.charging
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
+import scala.collection.mutable
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
/**
* In practice a resource usage will be charged for the total amount of usage
* between resource usage changes.
*
- * Example resource that might be adept to a continuous policy
- * is diskspace.
+ * Example resource that might be adept to a continuous policy is diskspace, as in Pithos+ service.
*
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
ChargingBehaviorAliases.continuous,
Set(ChargingBehaviorNameInput, UnitPriceInput, OldTotalAmountInput, TimeDeltaInput)) {
+ protected def computeSelectorPath(
+ chargingData: mutable.Map[String, Any],
+ currentResourceEvent: ResourceEventModel,
+ referenceTimeslot: Timeslot,
+ previousValue: Double,
+ totalCredits: Double,
+ oldAccumulatingAmount: Double,
+ newAccumulatingAmount: Double
+ ): List[String] = {
+ Nil
+ }
+
def computeNewAccumulatingAmount(oldAmount: Double, newEventValue: Double, details: Map[String, String]): Double = {
// If the total is in the details, get it, or else compute it
details.get("total") match {
+++ /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.charging
-
-import gr.grnet.aquarium.event.model.resource.ResourceEventModel
-import gr.grnet.aquarium.AquariumInternalError
-
-/**
- * An discrete charging behavior indicates that a resource should be charged directly
- * at each resource state change, i.e. the charging is not dependent on
- * the time the resource.
- *
- * Example oneoff resources might be individual charges applied to various
- * actions (e.g. the fact that a user has created an account) or resources
- * that should be charged per volume once (e.g. the allocation of a volume)
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-final class DiscreteChargingBehavior
- extends ChargingBehavior(
- ChargingBehaviorAliases.discrete,
- Set(ChargingBehaviorNameInput, UnitPriceInput, CurrentValueInput)) {
-
- def computeNewAccumulatingAmount(oldAmount: Double, newEventValue: Double, details: Map[String, String]): Double = {
- oldAmount + newEventValue
- }
-
- def getResourceInstanceInitialAmount: Double = {
- 0.0
- }
-
- /**
- * This is called when we have the very first event for a particular resource instance, and we want to know
- * if it is billable or not.
- */
- def isBillableFirstEvent(event: ResourceEventModel) = {
- false // nope, we definitely need a previous one.
- }
-
- // FIXME: Check semantics of this. I just put false until thorough study
- def mustGenerateDummyFirstEvent = false
-
- def supportsImplicitEvents = {
- false
- }
-
- def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = {
- false
- }
-
- def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, occurredMillis: Long) = {
- throw new AquariumInternalError("constructImplicitEndEventFor() Not compliant with %s".format(this))
- }
-}
-
-object DiscreteChargingBehavior {
- private[this] final val TheOne = new DiscreteChargingBehavior
-
- def apply(): DiscreteChargingBehavior = TheOne
-}
/**
* Encapsulates the possible states that a resource with an
- * [[gr.grnet.aquarium.charging.OnOffChargingBehavior]]
+ * [[gr.grnet.aquarium.charging.VMChargingBehavior]]
* can be.
*
* @author Christos KK Loverdos <loverdos@gmail.com>
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
import gr.grnet.aquarium.AquariumException
+import scala.collection.mutable
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
+import gr.grnet.aquarium.policy.FullPriceTable
/**
* A charging behavior for which resource events just carry a credit amount that will be added to the total one.
ChargingBehaviorAliases.once,
Set(ChargingBehaviorNameInput, CurrentValueInput)) {
+ protected def computeSelectorPath(
+ chargingData: mutable.Map[String, Any],
+ currentResourceEvent: ResourceEventModel,
+ referenceTimeslot: Timeslot,
+ previousValue: Double,
+ totalCredits: Double,
+ oldAccumulatingAmount: Double,
+ newAccumulatingAmount: Double
+ ): List[String] = {
+ List(FullPriceTable.DefaultSelectorKey)
+ }
/**
* This is called when we have the very first event for a particular resource instance, and we want to know
* if it is billable or not.
import gr.grnet.aquarium.{AquariumInternalError, AquariumException}
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
+import scala.collection.mutable
+import VMChargingBehavior.Selectors.Power
+import gr.grnet.aquarium.policy.{FullPriceTable, EffectivePriceTable}
/**
- * An onoff charging behavior expects a resource to be in one of the two allowed
- * states (`on` and `off`, respectively). It will charge for resource usage
- * within the timeframes specified by consecutive on and off resource events.
- * An onoff policy is the same as a continuous policy, except for
- * the timeframes within the resource is in the `off` state.
- *
- * Example resources that might be adept to onoff policies are VMs in a
- * cloud application and books in a book lending application.
+ * The new [[gr.grnet.aquarium.charging.ChargingBehavior]] for VMs usage.
*
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-final class OnOffChargingBehavior
+final class VMChargingBehavior
extends ChargingBehavior(
- ChargingBehaviorAliases.onoff,
- Set(ChargingBehaviorNameInput, UnitPriceInput, TimeDeltaInput)) {
-
+ ChargingBehaviorAliases.vmtime,
+ Set(ChargingBehaviorNameInput, UnitPriceInput, TimeDeltaInput),
+ List(List(Power.powerOn, Power.powerOff))) {
+
+ protected def computeSelectorPath(
+ chargingData: mutable.Map[String, Any],
+ currentResourceEvent: ResourceEventModel,
+ referenceTimeslot: Timeslot,
+ previousValue: Double,
+ totalCredits: Double,
+ oldAccumulatingAmount: Double,
+ newAccumulatingAmount: Double
+ ): List[String] = {
+ // FIXME
+ List(Power.powerOn) // compute prices for power-on state
+ }
/**
*
* @param oldAmount is ignored
0.0
}
- private[this]
- def getValueForCreditCalculation(oldAmount: Double, newEventValue: Double): Double = {
- import OnOffChargingBehaviorValues.{ON, OFF}
-
- def exception(rs: OnOffPolicyResourceState) =
- new AquariumException("Resource state transition error (%s -> %s)".format(rs, rs))
-
- (oldAmount, newEventValue) match {
- case (ON, ON) ⇒
- throw exception(OnResourceState)
- case (ON, OFF) ⇒
- OFF
- case (OFF, ON) ⇒
- ON
- case (OFF, OFF) ⇒
- throw exception(OffResourceState)
- }
- }
-
override def isBillableEvent(event: ResourceEventModel) = {
// ON events do not contribute, only OFF ones.
- OnOffChargingBehaviorValues.isOFFValue(event.value)
+ VMChargingBehaviorValues.isOFFValue(event.value)
}
/**
def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = {
// If we have ON events with no OFF companions at the end of the billing period,
// then we must generate implicit OFF events.
- OnOffChargingBehaviorValues.isONValue(resourceEvent.value)
+ VMChargingBehaviorValues.isONValue(resourceEvent.value)
}
def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, newOccurredMillis: Long) = {
assert(supportsImplicitEvents && mustConstructImplicitEndEventFor(resourceEvent))
- assert(OnOffChargingBehaviorValues.isONValue(resourceEvent.value))
+ assert(VMChargingBehaviorValues.isONValue(resourceEvent.value))
val details = resourceEvent.details
val newDetails = ResourceEventModel.setAquariumSyntheticAndImplicitEnd(details)
- val newValue = OnOffChargingBehaviorValues.OFF
+ val newValue = VMChargingBehaviorValues.OFF
resourceEvent.withDetailsAndValue(newDetails, newValue, newOccurredMillis)
}
}
}
-object OnOffChargingBehavior {
- private[this] final val TheOne = new OnOffChargingBehavior
-
- def apply(): OnOffChargingBehavior = TheOne
+object VMChargingBehavior {
+ object Selectors {
+ object Power {
+ // When the VM is powered on
+ final val powerOn = "powerOn"
+ // When the VM is powered off
+ final val powerOff = "powerOff"
+ }
+ }
}
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-object OnOffChargingBehaviorValues {
- final val ON = 1.0
- final val OFF = 0.0
+object VMChargingBehaviorValues {
+ final val DESTROY = 2.0
+ final val ON = 1.0
+ final val OFF = 0.0
- def isONValue (value: Double) = value == ON
- def isOFFValue(value: Double) = value == OFF
+ def isDESTROYValue(value: Double) = value == DESTROY
+ def isONValue (value: Double) = value == ON
+ def isOFFValue (value: Double) = value == OFF
}
import com.ckkloverdos.maybe.Just
import gr.grnet.aquarium.policy.ResourceType
import gr.grnet.aquarium.policy.EffectiveUnitPrice
-import gr.grnet.aquarium.charging.Chargeslot
+import gr.grnet.aquarium.charging.{ChargingBehavior, Chargeslot}
/**
* Methods for converting accounting events to wallet entries.
policy: PolicyModel,
agreement: UserAgreementModel,
resourceType: ResourceType,
+ effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable,
clogOpt: Option[ContextualLogger] = None
): SortedMap[Timeslot, Double] = {
val clog = ContextualLogger.fromOther(clogOpt, logger, "resolveEffectiveUnitPrices()")
// Note that most of the code is taken from calcChangeChunks()
- val ret = resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType)
+ val ret = resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType, effectivePriceTableSelector)
ret map {case (t,p) => (t,p.unitPrice)}
}
resourceType: ResourceType,
policyByTimeslot: SortedMap[Timeslot, PolicyModel],
agreementByTimeslot: SortedMap[Timeslot, UserAgreementModel],
+ effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable,
clogOpt: Option[ContextualLogger] = None
): List[Chargeslot] = {
//val userAgreement = agreementByTimeslot.valuesIterator.next()//getAgreementWithin(alignedTimeslot)
val userAgreement = getAgreementWithin(alignedTimeslot)
- // TODO: Factor this out, just like we did with:
- // TODO: val alignedTimeslots = splitTimeslotByPoliciesAndAgreements
- // Note that most of the code is already taken from calcChangeChunks()
- val unitPriceByTimeslot = resolveEffectiveUnitPrices(alignedTimeslot, policy, userAgreement, resourceType, Some(clog))
+ val unitPriceByTimeslot = resolveEffectiveUnitPrices(
+ alignedTimeslot,
+ policy,
+ userAgreement,
+ resourceType,
+ effectivePriceTableSelector,
+ Some(clog)
+ )
// Now, the timeslots must be the same
val finegrainedTimeslots = unitPriceByTimeslot.keySet
* 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
- }
+ private[this] def resolveEffectiveUnitPricesForTimeslot(
+ alignedTimeslot: Timeslot,
+ policy: PolicyModel,
+ agreement: UserAgreementModel,
+ resourceType: ResourceType,
+ effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable
+ ): PriceMap = {
+
+ val fullPriceTable = agreement.computeFullPriceTable(policy)
+ val effectivePriceTable = effectivePriceTableSelector(fullPriceTable)
resolveEffective(alignedTimeslot, effectivePriceTable.priceOverrides)
//immutable.SortedMap(alignedTimeslot -> effectivePriceTable.priceOverrides.head)
import gr.grnet.aquarium.util.{makeString, UTF_8_Charset}
import java.nio.charset.Charset
+import gr.grnet.aquarium.policy.{ResourceType, EffectiveUnitPrice, EffectivePriceTable, FullPriceTable, StdPolicy}
+import gr.grnet.aquarium.charging.state.WorkingUserState
/**
* Provides conversion methods from and to JSON.
/**
* The application-wide JSON formats used from the underlying lift-json library.
*/
- implicit final val Formats = (DefaultFormats ++ JodaTimeSerializers.all)
+// implicit final val Formats = (DefaultFormats ++ JodaTimeSerializers.all)
+ final val StdPolicyFormats = Serialization.formats(FullTypeHints(List(
+ // gather here all the "difficult" classes
+
+ // [[PolicyModel]]
+ classOf[StdPolicy],
+ classOf[ResourceType], // It is OK to leave this out
+ classOf[FullPriceTable],
+ classOf[EffectivePriceTable],
+ classOf[EffectiveUnitPrice],
+
+ // [[WorkingUserState]]
+ classOf[WorkingUserState]
+ )))
+ final val JodaFormats = JodaTimeSerializers.all
// implicit final val Formats = (DefaultFormats.withHints(FullTypeHints(List(classOf[AnyRef]))) ++ JodaTimeSerializers.all)
+ implicit final val Formats: Formats = StdPolicyFormats ++ JodaFormats
// Serialization.formats(FullTypeHints(List(classOf[AnyRef])))
+// final val PolicyModelSerializer: Serializer[PolicyModel] = new Serializer[PolicyModel] {
+// def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, _root_.net.liftweb.json.JValue), PolicyModel] = {
+// case (tpe @ TypeInfo(_, _), jValue) ⇒
+// println("!! tpe = " + tpe)
+// println("!! jValue = " + jValue)
+// Extraction.extract(jValue, tpe)(FullFormats).asInstanceOf[PolicyModel]
+// }
+//
+// def serialize(implicit format: Formats): PartialFunction[Any, _root_.net.liftweb.json.JValue] = {
+// case x: PolicyModel ⇒
+// println("GOT a policymodel")
+// Extraction.decompose(x)(FullFormats)
+// }
+// }
/**
* Converts a value to JSON AST (Abstract Syntax Tree) by acting a bit intelligently, depending on the actual type
import com.mongodb.util.JSON
import com.mongodb.DBObject
import xml.NodeSeq
-import net.liftweb.json.Xml
+import net.liftweb.json.{Printer, JsonAST, Extraction, Xml}
+import gr.grnet.aquarium.policy.PolicyModel
/**
private[this] final lazy val builder: ConvertersBuilder = {
val builder: ConvertersBuilder = new StdConvertersBuilder().registerDefaultConversions()
+ // PolicyModel ⇒ JValue
+// builder.registerConverter(PolicyModelToJValueConverter)
+
+ // PolicyModel ⇒ PrettyJsonTextFormat
+// builder.registerConverter(PolicyModelToPrettyJsonTextFormatConverter)
+
+ // PolicyModel ⇒ CompactJsonTextFormat
+// builder.registerConverter(PolicyModelToCompactJsonTextFormatConverter)
+
// Any ⇒ JValue
builder.registerConverter(AnyToJValueConverter)
// Any ⇒ CompactJsonTextFormat
builder.registerConverter(AnyToCompactJsonTextConverter)
+ // JsonTextFormat ⇒ PolicyModel
+// builder.registerConverter(JsonTextToPolicyModelConverter)
+
// JsonTextFormat ⇒ AnyRef
builder.registerConverter(JsonTextToObjectConverter)
builder
}
+// object PolicyModelToJValueConverter
+// extends NonStrictSourceConverterSkeleton[PolicyModel, JValue] {
+// protected def convertEx_(sourceValue: PolicyModel): JValue = {
+// Extraction.decompose(sourceValue)(JsonConversions.FullFormats)
+// }
+// }
+
+// object PolicyModelToPrettyJsonTextFormatConverter
+// extends NonStrictSourceConverterSkeleton[PolicyModel, PrettyJsonTextFormat] {
+// protected def convertEx_(sourceValue: PolicyModel): PrettyJsonTextFormat = {
+// val jValue = Extraction.decompose(sourceValue)(JsonConversions.FullFormats)
+// val jDoc = JsonAST.render(jValue)
+// PrettyJsonTextFormat(Printer.pretty(jDoc))
+// }
+// }
+
+// object PolicyModelToCompactJsonTextFormatConverter
+// extends NonStrictSourceConverterSkeleton[PolicyModel, CompactJsonTextFormat] {
+// protected def convertEx_(sourceValue: PolicyModel): CompactJsonTextFormat = {
+// val jValue = Extraction.decompose(sourceValue)(JsonConversions.FullFormats)
+// val jDoc = JsonAST.render(jValue)
+// CompactJsonTextFormat(Printer.compact(jDoc))
+// }
+// }
+
object ByteArrayToJsonTextConverter extends StrictSourceConverterSkeleton[Array[Byte], JsonTextFormat] {
@throws(classOf[ConverterException])
final protected def convertEx_(sourceValue: Array[Byte]) = {
}
}
+// object JsonTextToPolicyModelConverter extends Converter {
+// def isStrictSource = false
+//
+// def canConvertType[S: Type, T: Type] = {
+// erasureOf[JsonTextFormat].isAssignableFrom(erasureOf[S])
+// }
+//
+// @throws(classOf[ConverterException])
+// def convertEx[T: Type](sourceValue: Any) = {
+// // Generic deserializer from json string to a business logic model
+// JsonConversions.jsonToObject[T](sourceValue.asInstanceOf[JsonTextFormat].value)(manifest[T], JsonConversions.FullFormats)
+// }
+// }
+
object JsonSupportToDBObjectConverter extends NonStrictSourceConverterSkeleton[JsonSupport, DBObject] {
@throws(classOf[ConverterException])
final protected def convertEx_(sourceValue: JsonSupport) = {
import java.util.Date
import gr.grnet.aquarium.util.date.MutableDateCalc
import gr.grnet.aquarium.event.model.ExternalEventModel
+import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters}
+import gr.grnet.aquarium.policy.StdPolicy
/**
* The model of any resource event.
* @author Prodromos Gerakios <pgerakios@grnet.gr>
*/
-case class EffectiveUnitPrice(unitPrice: Double, when: Option[(CronSpec,CronSpec)]) { // TODO: use DSLTimeframe
+case class EffectiveUnitPrice(unitPrice: Double, when: Option[(CronSpec,CronSpec)] = None) {
/* 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
package gr.grnet.aquarium.policy
+import gr.grnet.aquarium.AquariumInternalError
+import scala.annotation.tailrec
+import gr.grnet.aquarium.util.shortNameOfType
+
/**
* A full price table provides detailed pricing information for all resources.
*
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-case class FullPriceTable(perResource: Map[String/*Resource*/, EffectivePriceTable]) {
- def effectivePriceTableForResourceType(resourceType: String): Option[EffectivePriceTable] = {
- perResource.get(resourceType)
- }
+case class FullPriceTable(
+ perResource: Map[String/*Resource*/,
+ Map[String/*Per-ChargingBehavior Key, "default" is the default*/, Any]]
+) {
+
+ def effectivePriceTableOfSelectorForResource(
+ selectorPath: List[String],
+ resource: String
+ ): EffectivePriceTable = {
+
+ @tailrec
+ def find(
+ selectorPath: List[String],
+ selectorData: Any
+ ): EffectivePriceTable = {
+
+ selectorPath match {
+ case Nil ⇒
+ // End of selector path. This means that the data must be an EffectivePriceTable
+ selectorData match {
+ case ept: EffectivePriceTable ⇒
+ ept
- def effectivePriceTableForResourceType(resourceType: ResourceType): Option[EffectivePriceTable] = {
- perResource.get(resourceType.name)
+ case _ ⇒
+ // TODO more informative error message (include selector path, resource?)
+ throw new AquariumInternalError("Got %s instead of an %s", selectorData, shortNameOfType[EffectivePriceTable])
+ }
+
+ case key :: tailSelectorPath ⇒
+ // Intermediate path. This means we have another round of Map[String, Any]
+ selectorData match {
+ case selectorMap: Map[_, _] ⇒
+ selectorMap.asInstanceOf[Map[String, _]].get(key) match {
+ case None ⇒
+ throw new AquariumInternalError("Did not find value for selector %s", key)
+
+ case Some(nextSelectorData) ⇒
+ find(tailSelectorPath, nextSelectorData)
+ }
+ }
+ }
+ }
+
+ val selectorDataOpt = perResource.get(resource)
+ if(selectorDataOpt.isEmpty) {
+ throw new AquariumInternalError("Unknown resource type '%s'", resource)
+ }
+ val selectorData = selectorDataOpt.get
+
+ find(selectorPath, selectorData)
}
}
+
+object FullPriceTable {
+ final val DefaultSelectorKey = "default"
+}
\ No newline at end of file
import gr.grnet.aquarium.{AquariumInternalError, Timespan}
import gr.grnet.aquarium.util.json.JsonSupport
import gr.grnet.aquarium.charging.ChargingBehavior
+import gr.grnet.aquarium.converter.{JsonTextFormat, StdConverters}
/**
* A policy is the fundamental business-related configuration of Aquarium.
}
final object Names extends NamesT
+
+ def fromJsonString(json: String): PolicyModel = {
+ StdConverters.AllConverters.convertEx[StdPolicy](JsonTextFormat(json))
+ }
}
package gr.grnet.aquarium.policy
-import gr.grnet.aquarium.Timespan
+import gr.grnet.aquarium.{AquariumInternalError, Timespan}
+import gr.grnet.aquarium.charging.ChargingBehavior
/**
*
1
}
}
+
+ def computeFullPriceTable(policy: PolicyModel): FullPriceTable = {
+ this.fullPriceTableRef match {
+ case PolicyDefinedFullPriceTableRef ⇒
+ policy.roleMapping.get(role) match {
+ case Some(fullPriceTable) ⇒
+ fullPriceTable
+
+ case None ⇒
+ throw new AquariumInternalError("Unknown role '%s' in policy".format(role))
+ }
+
+ case AdHocFullPriceTableRef(fullPriceTable) ⇒
+ fullPriceTable
+ }
+ }
}
+++ /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.simulation
-import gr.grnet.aquarium.policy.PolicyModel
-import gr.grnet.aquarium.charging.{DiscreteChargingBehavior, ChargingBehavior}
-
-
-/**
- * A simulator for the standard `bandwidth` resource.
- *
- * @author Christos KK Loverdos <loverdos@gmail.com>
- */
-
-class StdBandwidthResourceSim(
- name: String = StdVMTimeResourceSim.DSLNames.name,
- unit: String = StdVMTimeResourceSim.DSLNames.unit,
- chargingBehavior: String = classOf[DiscreteChargingBehavior].getName
-) extends ResourceSim(name, unit, chargingBehavior) {
-
-override def newInstance(instanceId: String, owner: UserSim, client: ClientSim) =
- StdBandwidthInstanceSim(this, instanceId, owner, client)
-}
-
-
-object StdBandwidthResourceSim {
- object DSLNames {
- final val name = "bandwidth"
- final val unit = "MB/Hr"
- }
-
- def fromPolicy(policy: PolicyModel): StdBandwidthResourceSim = {
- val resourceType = policy.resourceTypesMap(DSLNames.name)
- new StdBandwidthResourceSim(
- resourceType.name,
- resourceType.unit,
- resourceType.chargingBehavior
- )
- }
-}
-
import java.util.Date
import gr.grnet.aquarium.util.date.MutableDateCalc
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
-import gr.grnet.aquarium.charging.OnOffChargingBehaviorValues
+import gr.grnet.aquarium.charging.VMChargingBehaviorValues
/**
* A simulator for an instance of the standard `vmtime` resource.
newResourceEvent(
occurredDate.getTime,
occurredDate.getTime,
- OnOffChargingBehaviorValues.ON,
+ VMChargingBehaviorValues.ON,
details,
eventVersion
)
newResourceEvent(
occurredDate.getTime,
occurredDate.getTime,
- OnOffChargingBehaviorValues.OFF,
+ VMChargingBehaviorValues.OFF,
details,
eventVersion
)
newResourceEvent(
occurredTime,
receivedTime,
- OnOffChargingBehaviorValues.OFF,
+ VMChargingBehaviorValues.OFF,
details,
eventVersion
)
package gr.grnet.aquarium.simulation
import gr.grnet.aquarium.policy.PolicyModel
-import gr.grnet.aquarium.charging.{OnOffChargingBehavior, ChargingBehavior}
+import gr.grnet.aquarium.charging.{VMChargingBehavior, ChargingBehavior}
/**
class StdVMTimeResourceSim(
name: String = StdVMTimeResourceSim.DSLNames.name,
unit: String = StdVMTimeResourceSim.DSLNames.unit,
- chargingBehavior: String = classOf[OnOffChargingBehavior].getName
+ chargingBehavior: String = classOf[VMChargingBehavior].getName
) extends ResourceSim(name, unit, chargingBehavior) {
override def newInstance(instanceId: String, owner: UserSim, client: ClientSim) =
}
@inline
- def StartStopErrorHandler[S](logger: Logger,
- message: String,
- onException: ⇒ Unit = {}): PartialFunction[Throwable, Unit] = {
+ def StartStopErrorHandler[S](
+ logger: Logger,
+ message: String,
+ onException: ⇒ Unit = {}
+ ): PartialFunction[Throwable, Unit] = {
case e: Throwable ⇒
safeUnit(onException)
afterLastIndexOf(".", manifest[C].toString)
}
+ def nameOfClass[C: ClassManifest] = classManifest[C].erasure.getName
+
/**
* Compute the class name excluding any leading packages and any `$` prefixes.
*
"[%s] %s".format(obj.getClass, obj)
}
- /**
- * This basically turns an [[scala.Option]] into a [[com.ckkloverdos.maybe.Maybe]] when asking a
- * [[scala.collection.Map]] for a key.
- *
- * @param map The input map.
- * @param key The key we are interested in.
- * @tparam A The type of keys.
- * @tparam B The type of values.
- *
- * @return A [[com.ckkloverdos.maybe.Just]] if a value was found, a
- * [[com.ckkloverdos.maybe.NoVal]] if nothing was found and a
- * [[com.ckkloverdos.maybe.Failed]] if some error happened.
- */
- @inline
- def findFromMapAsMaybe[A, B <: AnyRef](map: scala.collection.Map[A, B], key: A): Maybe[B] = Maybe {
- map.get(key) match {
- case Some(value) ⇒
- value
- case None ⇒
- null.asInstanceOf[B]
- }
- }
-
- @inline
- def findAndRemoveFromMap[A, B <: AnyRef](map: scala.collection.mutable.Map[A, B], key: A): Option[B] = {
- map.remove(key)
- }
-
// Dear scalac. Optimize this.
def nspaces(n: Int): String = {
("" /: (1 to n)) ((string, _) => string + " ")
<logger name="ch.qos.logback" level="DEBUG"/>
- <logger name="com.ckkloverdos" level="DEBUG"/>
+ <logger name="com.ckkloverdos" level="INFO"/>
<logger name="gr.grnet" level="DEBUG"/>
-{
- "id":"3F8A9777-8C12-4529-B8E4-256AD840BEF2",
+{ "jsonClass":"gr.grnet.aquarium.policy.StdPolicy",
+
+ "id":"test-default-policy",
"validityTimespan":{
"fromMillis":0,
"toMillis":9223372036854775807
},
- "resourceTypes":[
- {
- "name":"bandwidth",
- "unit":"MB/Hr",
- "chargingBehavior":"gr.grnet.aquarium.charging.DiscreteChargingBehavior"
- },
- {
- "name":"vmtime",
- "unit":"Hr",
- "chargingBehavior":"gr.grnet.aquarium.charging.OnOffChargingBehavior"
- },
- {
- "name":"diskspace",
- "unit":"MB/Hr",
- "chargingBehavior":"gr.grnet.aquarium.charging.ContinuousChargingBehavior"
- }
- ],
+ "resourceTypes":[{
+ "jsonClass":"gr.grnet.aquarium.policy.ResourceType",
+ "name":"diskspace",
+ "unit":"MB/Hr",
+ "chargingBehavior":"gr.grnet.aquarium.charging.ContinuousChargingBehavior"
+ },{
+ "jsonClass":"gr.grnet.aquarium.policy.ResourceType",
+ "name":"vmtime",
+ "unit":"Hr",
+ "chargingBehavior":"gr.grnet.aquarium.charging.VMChargingBehavior"
+ },{
+ "jsonClass":"gr.grnet.aquarium.policy.ResourceType",
+ "name":"addcredits",
+ "unit":"credits",
+ "chargingBehavior":"gr.grnet.aquarium.charging.OnceChargingBehavior"
+ }],
"chargingBehaviors":[
- "gr.grnet.aquarium.charging.DiscreteChargingBehavior",
- "gr.grnet.aquarium.charging.OnOffChargingBehavior",
+ "gr.grnet.aquarium.charging.VMChargingBehavior",
"gr.grnet.aquarium.charging.ContinuousChargingBehavior",
"gr.grnet.aquarium.charging.OnceChargingBehavior"
],
-
+
"roleMapping":{
"default":{
+ "jsonClass":"gr.grnet.aquarium.policy.FullPriceTable",
+
"perResource":{
- "bandwidth":{
- "priceOverrides":[
- {
+ "diskspace":{
+ "default":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+ "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
"unitPrice":0.01
- }
- ]
+ }]
+ }
},
+
"vmtime":{
- "priceOverrides":[
- {
+ "powerOn":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+ "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
"unitPrice":0.01
- }
- ]
+ }]
+ },
+ "powerOff":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+ "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
+ "unitPrice":0.0010
+ }]
+ }
},
- "diskspace":{
- "priceOverrides":[
- {
- "unitPrice":0.01
- }
- ]
+
+ "addcredits":{
+ "default":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+ "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
+ "unitPrice":-1.0
+ }]
+ }
}
}
}
}
-}
\ No newline at end of file
+}
* or implied, of GRNET S.A.
*/
-package gr.grnet.aquarium.simulation
+package gr.grnet.aquarium.event.model
-import java.util.Date
-import gr.grnet.aquarium.store.RecordID
+import org.junit.Test
+import gr.grnet.aquarium.event.model.resource.StdResourceEvent
/**
- * A simulator for an instance of the standard `bandwidth` resource.
*
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-case class StdBandwidthInstanceSim(override val resource: StdBandwidthResourceSim,
- override val instanceId: String,
- override val owner: UserSim,
- override val client: ClientSim)
-extends ResourceInstanceSim(resource, instanceId, owner, client) {
- def useBandwidth(occurredDate: Date,
- megaBytes: Double,
- details: Map[String, String] = Map(),
- eventVersion: String = "1.0") = {
+class StdResourceEventTest {
+ @Test
+ def testJson() {
+ val rc = StdResourceEvent("id-2", 1000L, 1000L, "luv@g.com", "pithos", "disk", "/disk1", 1.0, "1.0", Map())
+ val json = rc.toJsonString
+ val obj = StdResourceEvent.fromJsonString(json)
- newResourceEvent(
- occurredDate.getTime,
- occurredDate.getTime,
- megaBytes,
- details,
- eventVersion
- )
+ assert(rc == obj)
}
-}
\ No newline at end of file
+}
import org.junit.Test
import gr.grnet.aquarium.Timespan
-import gr.grnet.aquarium.charging.{OnceChargingBehavior, ContinuousChargingBehavior, OnOffChargingBehavior, DiscreteChargingBehavior}
-import gr.grnet.aquarium.converter.{StdConverters, PrettyJsonTextFormat}
+import gr.grnet.aquarium.charging.{OnceChargingBehavior, ContinuousChargingBehavior, VMChargingBehavior}
+import gr.grnet.aquarium.charging.VMChargingBehavior.Selectors.Power
+import gr.grnet.aquarium.util.nameOfClass
+import FullPriceTable.DefaultSelectorKey
/**
*
class StdPolicyTest {
final lazy val policy = StdPolicy(
- id = "policy-1",
+ id = "default-policy",
parentID = None,
validityTimespan = Timespan(0),
resourceTypes = Set(
- ResourceType("bandwidth", "MB/Hr", classOf[DiscreteChargingBehavior].getName),
- ResourceType("vmtime", "Hr", classOf[OnOffChargingBehavior].getName),
- ResourceType("diskspace", "MB/Hr", classOf[ContinuousChargingBehavior].getName)
+ ResourceType("diskspace", "MB/Hr", nameOfClass[ContinuousChargingBehavior]),
+ ResourceType("vmtime", "Hr", nameOfClass[VMChargingBehavior])
),
chargingBehaviors = Set(
- classOf[DiscreteChargingBehavior].getName,
- classOf[OnOffChargingBehavior].getName,
- classOf[ContinuousChargingBehavior].getName,
- classOf[OnceChargingBehavior].getName
+ nameOfClass[VMChargingBehavior],
+ nameOfClass[ContinuousChargingBehavior],
+ nameOfClass[OnceChargingBehavior]
),
roleMapping = Map(
"default" -> FullPriceTable(Map(
- "bandwidth" -> EffectivePriceTable(EffectiveUnitPrice(0.01, None) :: Nil),
- "vmtime" -> EffectivePriceTable(EffectiveUnitPrice(0.01, None) :: Nil),
- "diskspace" -> EffectivePriceTable(EffectiveUnitPrice(0.01, None) :: Nil)
+ "diskspace" -> Map(
+ DefaultSelectorKey -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil)
+ ),
+ "vmtime" -> Map(
+ Power.powerOn -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil),
+ Power.powerOff -> EffectivePriceTable(EffectiveUnitPrice(0.001) :: Nil) // cheaper when the VM is OFF
+ )
))
)
)
@Test
def testJson(): Unit = {
- val converters = StdConverters.AllConverters
- val json = converters.convertEx[PrettyJsonTextFormat](policy)
- val obj = converters.convertEx[StdPolicy](json)
+ val json = policy.toJsonString
+ val obj = PolicyModel.fromJsonString(json)
+
+ println(json)
assert(policy == obj)
}
import gr.grnet.aquarium.util.date.MutableDateCalc
import gr.grnet.aquarium.computation.BillingMonthInfo
import gr.grnet.aquarium.charging._
-import gr.grnet.aquarium.policy.{PolicyDefinedFullPriceTableRef, StdUserAgreement, EffectiveUnitPrice, EffectivePriceTable, FullPriceTable, ResourceType, StdPolicy, PolicyModel}
-import gr.grnet.aquarium.Timespan
-import gr.grnet.aquarium.charging.state.UserStateBootstrap
+import gr.grnet.aquarium.policy.{PolicyDefinedFullPriceTableRef, FullPriceTable, PolicyModel}
import gr.grnet.aquarium.charging.reason.{NoSpecificChargingReason, MonthlyBillChargingReason}
import gr.grnet.aquarium.charging.state.WorkingUserState
+import gr.grnet.aquarium.policy.FullPriceTable._
+import gr.grnet.aquarium.Timespan
+import gr.grnet.aquarium.simulation.AquariumSim
+import scala.Some
+import gr.grnet.aquarium.policy.EffectiveUnitPrice
+import gr.grnet.aquarium.policy.ResourceType
+import gr.grnet.aquarium.policy.StdUserAgreement
+import gr.grnet.aquarium.charging.state.UserStateBootstrap
+import gr.grnet.aquarium.policy.EffectivePriceTable
+import gr.grnet.aquarium.policy.StdPolicy
+import gr.grnet.aquarium.simulation.ClientSim
/**
parentID = None,
validityTimespan = Timespan(0),
resourceTypes = Set(
- ResourceType("bandwidth", "MB/Hr", classOf[DiscreteChargingBehavior].getName),
- ResourceType("vmtime", "Hr", classOf[OnOffChargingBehavior].getName),
+ ResourceType("vmtime", "Hr", classOf[VMChargingBehavior].getName),
ResourceType("diskspace", "MB/Hr", classOf[ContinuousChargingBehavior].getName)
),
chargingBehaviors = Set(
- classOf[DiscreteChargingBehavior].getName,
- classOf[OnOffChargingBehavior].getName,
+ classOf[VMChargingBehavior].getName,
classOf[ContinuousChargingBehavior].getName,
classOf[OnceChargingBehavior].getName
),
roleMapping = Map(
"default" -> FullPriceTable(Map(
- "bandwidth" -> EffectivePriceTable(EffectiveUnitPrice(BandwidthUnitPrice, None) :: Nil),
- "vmtime" -> EffectivePriceTable(EffectiveUnitPrice(VMTimeUnitPrice, None) :: Nil),
- "diskspace" -> EffectivePriceTable(EffectiveUnitPrice(DiskspaceUnitPrice, None) :: Nil)
+ "diskspace" -> Map(DefaultSelectorKey -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil)),
+ "vmtime" -> Map(DefaultSelectorKey -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil))
))
)
)
creditsForDiscrete(currentValue)
- case ChargingBehaviorAliases.onoff ⇒
+ case ChargingBehaviorAliases.vmtime ⇒
val unitPrice = vars(UnitPriceInput).asInstanceOf[Double]
val timeDelta = vars(TimeDeltaInput).asInstanceOf[Double]
Map(
ChargingBehaviorAliases.continuous -> "hrs(timeDelta) * oldTotalAmount * %s".format(ContinuousUnitPrice),
ChargingBehaviorAliases.discrete -> "currentValue * %s".format(DiscreteUnitPrice),
- ChargingBehaviorAliases.onoff -> "hrs(timeDelta) * %s".format(OnOffUnitPrice),
+ ChargingBehaviorAliases.vmtime -> "hrs(timeDelta) * %s".format(OnOffUnitPrice),
ChargingBehaviorAliases.once -> "currentValue"))
}
// Those StdXXXResourceSim are just for debugging convenience anyway, so they must match by design.
val VMTimeResourceSim = StdVMTimeResourceSim.fromPolicy(DefaultPolicy)
val DiskspaceResourceSim = StdDiskspaceResourceSim.fromPolicy(DefaultPolicy)
- val BandwidthResourceSim = StdBandwidthResourceSim.fromPolicy(DefaultPolicy)
// There are two client services, synnefo and pithos.
val TheUIDGenerator: UIDGenerator[_] = new ConcurrentVMLocalUIDGenerator
aquarium.policyStore.insertPolicy(DefaultPolicy)
- val AquariumSim_ = AquariumSim(List(VMTimeResourceSim, DiskspaceResourceSim, BandwidthResourceSim), aquarium.resourceEventStore)
+ val AquariumSim_ = AquariumSim(List(VMTimeResourceSim, DiskspaceResourceSim), aquarium.resourceEventStore)
val DefaultResourcesMap = DefaultPolicy.resourceTypesMap//AquariumSim_.resourcesMap
val UserCKKL = AquariumSim_.newUser("CKKL", UserCreationDate)
// - synnefo is for VMTime and Bandwidth
// - pithos is for Diskspace
val VMTimeInstanceSim = VMTimeResourceSim.newInstance ("VM.1", UserCKKL, Synnefo)
- val BandwidthInstanceSim = BandwidthResourceSim.newInstance("3G.1", UserCKKL, Synnefo)
val DiskInstanceSim = DiskspaceResourceSim.newInstance("DISK.1", UserCKKL, Pithos)
private[this]
DiskInstanceSim.consumeMB(diskConsumptionDate1, 99)
DiskInstanceSim.consumeMB(diskConsumptionDate2, 23)
- // 100MB 3G bandwidth
- val bwDateCalc = diskConsumptionDateCalc2.copy.goPlusDays(1)
- BandwidthInstanceSim.useBandwidth(bwDateCalc.toDate, 100.0)
-
// ... and one "future" event
DiskInstanceSim.consumeMB(
StartOfBillingYearDateCalc.copy.