# Override the user event store (if present, it will not be given by the store provider above)
#policy.store.class=
+# A time period in milliseconds for which we can tolerate stale parts regarding user state.
+user.state.timestamp.threshold=1
+
# Administrative REST API authorization cookie
admin.cookie=1
\ No newline at end of file
// This will be dynamically interpreted during Aquarium startup
StdPolicy(
- id = "policy-1",
- parentID = None,
+ id = "policy-1",
+ parentID = None,
- validityTimespan = Timespan(0),
+ validityTimespan = Timespan(0),
- resourceTypes = Set(
- ResourceType("bandwidth", "MB/Hr", DiscreteChargingBehavior),
- ResourceType("vmtime", "Hr", OnOffChargingBehavior),
- ResourceType("diskspace", "MB/Hr", ContinuousChargingBehavior)
- ),
+ resourceTypes = Set(
+ ResourceType("bandwidth", "MB/Hr", classOf[DiscreteChargingBehavior].getName),
+ ResourceType("vmtime", "Hr", classOf[OnOffChargingBehavior].getName),
+ ResourceType("diskspace", "MB/Hr", classOf[ContinuousChargingBehavior].getName)
+ ),
- chargingBehaviorClasses = Set(
- DiscreteChargingBehavior.getClass.getName,
- OnOffChargingBehavior.getClass.getName,
- ContinuousChargingBehavior.getClass.getName,
- OnceChargingBehavior.getClass.getName
- ),
+ chargingBehaviors = Set(
+ classOf[DiscreteChargingBehavior].getName,
+ classOf[OnOffChargingBehavior].getName,
+ classOf[ContinuousChargingBehavior].getName,
+ classOf[OnceChargingBehavior].getName
+ ),
- 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)
- ))
- )
- )
\ No newline at end of file
+ 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)
+ ))
+ )
+)
\ No newline at end of file
import com.ckkloverdos.sys.SysProp
import gr.grnet.aquarium.service.event.AquariumCreatedEvent
import gr.grnet.aquarium.policy.{PolicyDefinedFullPriceTableRef, StdUserAgreement, UserAgreementModel, ResourceType}
+import gr.grnet.aquarium.charging.ChargingBehavior
/**
*
final class Aquarium(env: Env) extends Lifecycle with Loggable {
import Aquarium.EnvKeys
+ @volatile private[this] var _chargingBehaviorMap = Map[String, ChargingBehavior]()
+
private[this] val _isStopping = new AtomicBoolean(false)
override def toString = "%s/v%s".format(getClass.getName, version)
/**
* Reflectively provide a new instance of a class and configure it appropriately.
*/
- def newInstance[C <: AnyRef](_class: Class[C], className: String): C = {
+ def newInstance[C <: AnyRef](_class: Class[C]): C = {
+ newInstance(_class.getName)
+ }
+
+ /**
+ * Reflectively provide a new instance of a class and configure it appropriately.
+ */
+ def newInstance[C <: AnyRef](className: String): C = {
val originalProps = apply(EnvKeys.originalProps)
val instanceM = MaybeEither(defaultClassLoader.loadClass(className).newInstance().asInstanceOf[C])
"default"
}
+ def chargingBehaviorOf(resourceType: ResourceType): ChargingBehavior = {
+ val className = resourceType.chargingBehavior
+ _chargingBehaviorMap.get(className) match {
+ case Some(chargingBehavior) ⇒
+ chargingBehavior
+
+ case _ ⇒
+ // It does not matter if this is entered by multiple threads and more than one instance of the same class
+ // is created. The returned instance is not meant to be cached.
+ val chargingBehavior = newInstance[ChargingBehavior](className)
+ _chargingBehaviorMap synchronized {
+ _chargingBehaviorMap = _chargingBehaviorMap.updated(className, chargingBehavior)
+ }
+
+ chargingBehavior
+ }
+ }
+
def defaultClassLoader = apply(EnvKeys.defaultClassLoader)
def resourceEventStore = apply(EnvKeys.resourceEventStore)
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-abstract class ChargingBehavior(val name: String, val inputs: Set[ChargingInput]) {
+abstract class ChargingBehavior(val alias: String, val inputs: Set[ChargingInput]) {
final lazy val inputNames = inputs.map(_.name)
unitPrice)
}
- def isNamed(aName: String): Boolean = aName == name
+ def isNamed(aName: String): Boolean = aName == alias
def needsPreviousEventForCreditAndAmountCalculation: Boolean = {
// If we need any variable that is related to the previous event
}
object ChargingBehavior {
- def apply(name: String): ChargingBehavior = {
- name match {
- case null ⇒
- throw new AquariumException("<null> charging behavior")
-
- case name ⇒ name.toLowerCase match {
- case ChargingBehaviorNames.onoff ⇒ OnOffChargingBehavior
- case ChargingBehaviorNames.discrete ⇒ DiscreteChargingBehavior
- case ChargingBehaviorNames.continuous ⇒ ContinuousChargingBehavior
- case ChargingBehaviorNames.once ⇒ ContinuousChargingBehavior
-
- case _ ⇒
- throw new AquariumException("Invalid charging behavior %s".format(name))
- }
- }
- }
-
def makeValueMapFor(
chargingBehavior: ChargingBehavior,
totalCredits: Double,
val inputs = chargingBehavior.inputs
var map = Map[ChargingInput, Any]()
- if(inputs contains ChargingBehaviorNameInput) map += ChargingBehaviorNameInput -> chargingBehavior.name
+ if(inputs contains ChargingBehaviorNameInput) map += ChargingBehaviorNameInput -> chargingBehavior.alias
if(inputs contains TotalCreditsInput ) map += TotalCreditsInput -> totalCredits
if(inputs contains OldTotalAmountInput) map += OldTotalAmountInput -> oldTotalAmount
if(inputs contains NewTotalAmountInput) map += NewTotalAmountInput -> newTotalAmount
map
}
}
-
-/**
- * A charging behavior for which resource events just carry a credit amount that will be added to the total one.
- *
- * Examples are: a) Give a gift of X credits to the user, b) User bought a book, so charge for the book price.
- *
- */
-case object OnceChargingBehavior
-extends ChargingBehavior(
- ChargingBehaviorNames.once,
- Set(ChargingBehaviorNameInput, CurrentValueInput)
-) {
-
- /**
- * 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) = {
- true
- }
-
- def mustGenerateDummyFirstEvent = false // no need to
-
- def computeNewAccumulatingAmount(oldAmount: Double, newEventValue: Double, details: Map[String, String]) = {
- oldAmount
- }
-
- def getResourceInstanceInitialAmount = 0.0
-
- def supportsImplicitEvents = false
-
- def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = false
-
- def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, occurredMillis: Long) = {
- throw new AquariumException("constructImplicitEndEventFor() Not compliant with %s".format(this))
- }
-}
-
-/**
- * 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.
- */
-case object ContinuousChargingBehavior
-extends ChargingBehavior(
- ChargingBehaviorNames.continuous,
- Set(ChargingBehaviorNameInput, UnitPriceInput, OldTotalAmountInput, TimeDeltaInput)
-) {
-
- 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 {
- case Some(total) ⇒
- total.toDouble
-
- case _ ⇒
- 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) = {
- true
- }
-
- def mustGenerateDummyFirstEvent = true
-
- def supportsImplicitEvents = {
- true
- }
-
- def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = {
- true
- }
-
- def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, newOccurredMillis: Long) = {
- assert(supportsImplicitEvents && mustConstructImplicitEndEventFor(resourceEvent))
-
- val details = resourceEvent.details
- val newDetails = ResourceEventModel.setAquariumSyntheticAndImplicitEnd(details)
-
- resourceEvent.withDetails(newDetails, newOccurredMillis)
- }
-}
-
-/**
- * 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.
- */
-case object OnOffChargingBehavior
-extends ChargingBehavior(
- ChargingBehaviorNames.onoff,
- Set(ChargingBehaviorNameInput, UnitPriceInput, TimeDeltaInput)
-) {
-
- /**
- *
- * @param oldAmount is ignored
- * @param newEventValue
- * @return
- */
- def computeNewAccumulatingAmount(oldAmount: Double, newEventValue: Double, details: Map[String, String]): Double = {
- newEventValue
- }
-
- def getResourceInstanceInitialAmount: Double = {
- 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)
- }
-
- /**
- * 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
- }
-
- def mustGenerateDummyFirstEvent = false // should be handled by the implicit OFFs
-
- def supportsImplicitEvents = {
- true
- }
-
- 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)
- }
-
- def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, newOccurredMillis: Long) = {
- assert(supportsImplicitEvents && mustConstructImplicitEndEventFor(resourceEvent))
- assert(OnOffChargingBehaviorValues.isONValue(resourceEvent.value))
-
- val details = resourceEvent.details
- val newDetails = ResourceEventModel.setAquariumSyntheticAndImplicitEnd(details)
- val newValue = OnOffChargingBehaviorValues.OFF
-
- resourceEvent.withDetailsAndValue(newDetails, newValue, newOccurredMillis)
- }
-
- def constructImplicitStartEventFor(resourceEvent: ResourceEventModel) = {
- throw new AquariumInternalError("constructImplicitStartEventFor() Not compliant with %s".format(this))
- }
-}
-
-object OnOffChargingBehaviorValues {
- final val ON = 1.0
- final val OFF = 0.0
-
- def isONValue (value: Double) = value == ON
- def isOFFValue(value: Double) = value == OFF
-}
-
-/**
- * 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)
- */
-case object DiscreteChargingBehavior
-extends ChargingBehavior(
- ChargingBehaviorNames.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))
- }
-}
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-object ChargingBehaviorNames {
+object ChargingBehaviorAliases {
final val onoff = "onoff"
final val discrete = "discrete"
final val continuous = "continuous"
--- /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
+
+/**
+ * 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.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+final class ContinuousChargingBehavior
+ extends ChargingBehavior(
+ ChargingBehaviorAliases.continuous,
+ Set(ChargingBehaviorNameInput, UnitPriceInput, OldTotalAmountInput, TimeDeltaInput)) {
+
+ 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 {
+ case Some(total) ⇒
+ total.toDouble
+
+ case _ ⇒
+ 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) = {
+ true
+ }
+
+ def mustGenerateDummyFirstEvent = true
+
+ def supportsImplicitEvents = {
+ true
+ }
+
+ def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = {
+ true
+ }
+
+ def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, newOccurredMillis: Long) = {
+ assert(supportsImplicitEvents && mustConstructImplicitEndEventFor(resourceEvent))
+
+ val details = resourceEvent.details
+ val newDetails = ResourceEventModel.setAquariumSyntheticAndImplicitEnd(details)
+
+ resourceEvent.withDetails(newDetails, newOccurredMillis)
+ }
+}
+
+object ContinuousChargingBehavior {
+ private[this] final val TheOne = new ContinuousChargingBehavior
+
+ def apply(): ContinuousChargingBehavior = TheOne
+}
--- /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
+}
--- /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.{AquariumInternalError, AquariumException}
+import gr.grnet.aquarium.event.model.resource.ResourceEventModel
+
+/**
+ * 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.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+final class OnOffChargingBehavior
+ extends ChargingBehavior(
+ ChargingBehaviorAliases.onoff,
+ Set(ChargingBehaviorNameInput, UnitPriceInput, TimeDeltaInput)) {
+
+ /**
+ *
+ * @param oldAmount is ignored
+ * @param newEventValue
+ * @return
+ */
+ def computeNewAccumulatingAmount(oldAmount: Double, newEventValue: Double, details: Map[String, String]): Double = {
+ newEventValue
+ }
+
+ def getResourceInstanceInitialAmount: Double = {
+ 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)
+ }
+
+ /**
+ * 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
+ }
+
+ def mustGenerateDummyFirstEvent = false // should be handled by the implicit OFFs
+
+ def supportsImplicitEvents = {
+ true
+ }
+
+ 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)
+ }
+
+ def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, newOccurredMillis: Long) = {
+ assert(supportsImplicitEvents && mustConstructImplicitEndEventFor(resourceEvent))
+ assert(OnOffChargingBehaviorValues.isONValue(resourceEvent.value))
+
+ val details = resourceEvent.details
+ val newDetails = ResourceEventModel.setAquariumSyntheticAndImplicitEnd(details)
+ val newValue = OnOffChargingBehaviorValues.OFF
+
+ resourceEvent.withDetailsAndValue(newDetails, newValue, newOccurredMillis)
+ }
+
+ def constructImplicitStartEventFor(resourceEvent: ResourceEventModel) = {
+ throw new AquariumInternalError("constructImplicitStartEventFor() Not compliant with %s".format(this))
+ }
+}
+
+object OnOffChargingBehavior {
+ private[this] final val TheOne = new OnOffChargingBehavior
+
+ def apply(): OnOffChargingBehavior = TheOne
+}
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-object OnOffPolicyResourceStateNames {
- final val on = "on"
- final val off = "off"
+object OnOffChargingBehaviorValues {
+ final val ON = 1.0
+ final val OFF = 0.0
+
+ def isONValue (value: Double) = value == ON
+ def isOFFValue(value: Double) = value == OFF
}
+
def isOff: Boolean = !isOn
}
-object OnResourceState extends OnOffPolicyResourceState(OnOffPolicyResourceStateNames.on) {
+object OnResourceState extends OnOffPolicyResourceState(OnOffPolicyResourceState.Names.on) {
override def isOn = true
}
-object OffResourceState extends OnOffPolicyResourceState(OnOffPolicyResourceStateNames.off) {
+object OffResourceState extends OnOffPolicyResourceState(OnOffPolicyResourceState.Names.off) {
override def isOff = true
}
object OnOffPolicyResourceState {
- def apply(name: Any): OnOffPolicyResourceState = {
- name match {
- case x: String if (x.equalsIgnoreCase(OnOffPolicyResourceStateNames.on)) ⇒ OnResourceState
- case y: String if (y.equalsIgnoreCase(OnOffPolicyResourceStateNames.off)) ⇒ OffResourceState
- case a: Double if (a == 0) => OffResourceState
- case b: Double if (b == 1) => OnResourceState
- case i: Int if (i == 0) => OffResourceState
- case j: Int if (j == 1) => OnResourceState
- case _ => throw new AquariumException("Invalid OnOffPolicyResourceState %s".format(name))
- }
+ object Names {
+ final val on = "on"
+ final val off = "off"
}
}
--- /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.AquariumException
+
+/**
+ * A charging behavior for which resource events just carry a credit amount that will be added to the total one.
+ *
+ * Examples are: a) Give a gift of X credits to the user, b) User bought a book, so charge for the book price.
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+final class OnceChargingBehavior
+ extends ChargingBehavior(
+ ChargingBehaviorAliases.once,
+ Set(ChargingBehaviorNameInput, CurrentValueInput)) {
+
+ /**
+ * 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) = {
+ true
+ }
+
+ def mustGenerateDummyFirstEvent = false // no need to
+
+ def computeNewAccumulatingAmount(oldAmount: Double, newEventValue: Double, details: Map[String, String]) = {
+ oldAmount
+ }
+
+ def getResourceInstanceInitialAmount = 0.0
+
+ def supportsImplicitEvents = false
+
+ def mustConstructImplicitEndEventFor(resourceEvent: ResourceEventModel) = false
+
+ def constructImplicitEndEventFor(resourceEvent: ResourceEventModel, occurredMillis: Long) = {
+ throw new AquariumException("constructImplicitEndEventFor() Not compliant with %s".format(this))
+ }
+}
+
+object OnceChargingBehavior {
+ private[this] final val TheOne = new OnceChargingBehavior
+
+ def apply(): OnceChargingBehavior = TheOne
+}
import gr.grnet.aquarium.util.{ContextualLogger, Loggable}
import gr.grnet.aquarium.store.PolicyStore
import gr.grnet.aquarium.util.date.MutableDateCalc
-import gr.grnet.aquarium.AquariumInternalError
+import gr.grnet.aquarium.{Aquarium, AquariumInternalError}
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
import gr.grnet.aquarium.logic.accounting.algorithm.SimpleExecutableChargingBehaviorAlgorithm
import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
*
*/
def computeFullChargeslots(
+ aquarium: Aquarium,
previousResourceEventOpt: Option[ResourceEventModel],
currentResourceEvent: ResourceEventModel,
oldCredits: Double,
val occurredDate = currentResourceEvent.occurredDate
val occurredMillis = currentResourceEvent.occurredMillis
- val chargingBehavior = resourceType.chargingBehavior
+ val chargingBehavior = aquarium.chargingBehaviorOf(resourceType)
val (referenceTimeslot, policyByTimeslot, previousValue) = chargingBehavior.needsPreviousEventForCreditAndAmountCalculation match {
// We need a previous event
resourceTypesMap.get(theResource) match {
// We have a resource type (and thus a charging behavior)
case Some(resourceType) ⇒
- val chargingBehavior = resourceType.chargingBehavior
+ val chargingBehavior = aquarium.chargingBehaviorOf(resourceType)
clog.debug("%s for %s", chargingBehavior, resourceType)
val isBillable = chargingBehavior.isBillableEvent(currentResourceEvent)
if(!isBillable) {
// clog.debug("Computing full chargeslots")
val (referenceTimeslot, fullChargeslots) = timeslotComputations.computeFullChargeslots(
+ aquarium,
previousResourceEventOpt1,
currentResourceEvent,
oldCredits,
// Second, for the remaining events which must contribute an implicit OFF, we collect those OFFs
// ... in order to generate an implicit ON later (during the next billing cycle).
val (specialEvents, theirImplicitEnds) = userStateWorker.
- findAndRemoveGeneratorsOfImplicitEndEvents(billingMonthInfo.monthStopMillis)
+ findAndRemoveGeneratorsOfImplicitEndEvents(aquarium, billingMonthInfo.monthStopMillis)
if(specialEvents.lengthCompare(1) >= 0 || theirImplicitEnds.lengthCompare(1) >= 0) {
clog.debug("")
import gr.grnet.aquarium.event.model.resource.ResourceEventModel
import gr.grnet.aquarium.computation.state.parts.{IgnoredFirstResourceEventsWorker, ImplicitlyIssuedResourceEventsWorker, LatestResourceEventsWorker}
import gr.grnet.aquarium.policy.ResourceType
+import gr.grnet.aquarium.Aquarium
/**
* A helper object holding intermediate state/results during resource event processing.
* @see [[gr.grnet.aquarium.charging.ChargingBehavior]]
*/
def findAndRemoveGeneratorsOfImplicitEndEvents(
+ aquarium: Aquarium,
/**
* The `occurredMillis` that will be recorded in the synthetic implicit OFFs.
* Normally, this will be the end of a billing month.
for {
resourceEvent ← resourceEvents
resourceType ← resourceTypesMap.get(resourceEvent.safeResource)
- chargingBehavior = resourceType.chargingBehavior
+ chargingBehavior = aquarium.chargingBehaviorOf(resourceType)
} {
if(chargingBehavior.supportsImplicitEvents) {
if(chargingBehavior.mustConstructImplicitEndEventFor(resourceEvent)) {
package gr.grnet.aquarium.logic.accounting.algorithm
-import com.ckkloverdos.maybe.Maybe
-import gr.grnet.aquarium.logic.accounting.dsl._
import gr.grnet.aquarium.AquariumException
-import gr.grnet.aquarium.charging.{CurrentValueInput, TimeDeltaInput, OldTotalAmountInput, UnitPriceInput, ChargingBehaviorNames, ChargingBehaviorNameInput, ChargingInput}
+import gr.grnet.aquarium.charging.{CurrentValueInput, TimeDeltaInput, OldTotalAmountInput, UnitPriceInput, ChargingBehaviorAliases, ChargingBehaviorNameInput, ChargingInput}
/**
* An executable charging algorithm with some simple implementation.
def apply(vars: Map[ChargingInput, Any]): Double = {
vars.apply(ChargingBehaviorNameInput) match {
- case ChargingBehaviorNames.continuous ⇒
+ case ChargingBehaviorAliases.continuous ⇒
val unitPrice = vars(UnitPriceInput).asInstanceOf[Double]
val oldTotalAmount = vars(OldTotalAmountInput).asInstanceOf[Double]
val timeDelta = vars(TimeDeltaInput).asInstanceOf[Double]
hrs(timeDelta) * oldTotalAmount * unitPrice
- case ChargingBehaviorNames.discrete ⇒
+ case ChargingBehaviorAliases.discrete ⇒
val unitPrice = vars(UnitPriceInput).asInstanceOf[Double]
val currentValue = vars(CurrentValueInput).asInstanceOf[Double]
currentValue * unitPrice
- case ChargingBehaviorNames.onoff ⇒
+ case ChargingBehaviorAliases.onoff ⇒
val unitPrice = vars(UnitPriceInput).asInstanceOf[Double]
val timeDelta = vars(TimeDeltaInput).asInstanceOf[Double]
hrs(timeDelta) * unitPrice
- case ChargingBehaviorNames.once ⇒
+ case ChargingBehaviorAliases.once ⇒
val currentValue = vars(CurrentValueInput).asInstanceOf[Double]
currentValue
override def toString = "SimpleExecutableCostPolicyAlgorithm(%s)".format(
Map(
- ChargingBehaviorNames.continuous -> "hrs(timeDelta) * oldTotalAmount * unitPrice",
- ChargingBehaviorNames.discrete -> "currentValue * unitPrice",
- ChargingBehaviorNames.onoff -> "hrs(timeDelta) * unitPrice",
- ChargingBehaviorNames.once -> "currentValue"))
+ ChargingBehaviorAliases.continuous -> "hrs(timeDelta) * oldTotalAmount * unitPrice",
+ ChargingBehaviorAliases.discrete -> "currentValue * unitPrice",
+ ChargingBehaviorAliases.onoff -> "hrs(timeDelta) * unitPrice",
+ ChargingBehaviorAliases.once -> "currentValue"))
}
case None => "? ? ? ? ?"
case Some((_,s)) => s.toString
}
- override def toString : String = "EffectiveUnitPrice(%d,%s,%s)".
+ override def toString : String = "EffectiveUnitPrice(%f,%s,%s)".
format(unitPrice,stringOfStartCron,stringOfEndCron)
}
* Note than since a charging behavior is semantically attached to an implementation, a change in the set
* of known charging behaviors normally means a change in the implementation of Aquarium.
*/
- def chargingBehaviorClasses: Set[String/*ImplementationClassName*/]
+ def chargingBehaviors: Set[String/*ImplementationClassName*/]
/**
* Each role is mapped to a full price table.
package gr.grnet.aquarium.policy
-import gr.grnet.aquarium.charging.ChargingBehavior
/**
* The definition of a resource type. A resource type is a broad classification to which resource instances belong.
*
* @param name The name of this resource type. Examples are `vmtime`, `diskspace`, `bandwidth`, `diskio`.
* @param unit The unit we use to count the resource usage.
- * @param chargingBehavior The charging behavior used for this resource types.
+ * @param chargingBehavior The fully qualified name of the [[gr.grnet.aquarium.charging.ChargingBehavior]]
+ * implementation used for this resource type.
*
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-case class ResourceType(name: String, unit: String, chargingBehavior: ChargingBehavior)
+case class ResourceType(name: String, unit: String, chargingBehavior: String)
parentID: Option[String],
validityTimespan: Timespan,
resourceTypes: Set[ResourceType],
- chargingBehaviorClasses: Set[String],
+ chargingBehaviors: Set[String],
roleMapping: Map[String/*Role*/, FullPriceTable]
) extends PolicyModel {
// Create new User Actor instance
logger.debug("Creating new UserActor instance for %s".format(userID))
val actorRef = _actorSystem.actorOf(Props.apply({
- aquarium.newInstance(classOf[UserActor], classOf[UserActor].getName)
+ aquarium.newInstance(classOf[UserActor])
}), "userActor::%s".format(userID))
// Cache it for subsequent calls
* @author Christos KK Loverdos <loverdos@gmail.com>
*/
-class ResourceSim(val name: String, val unit: String, val chargingBehavior: ChargingBehavior ) {
+class ResourceSim(val name: String, val unit: String, val chargingBehavior: String) {
def toResourceType = ResourceType(name, unit, chargingBehavior)
object ResourceSim {
def apply(name: String, unit: String, chargingBehavior: ChargingBehavior) = {
- new ResourceSim(name, unit, chargingBehavior)
+ new ResourceSim(name, unit, chargingBehavior.getClass.getName)
}
}
class StdBandwidthResourceSim(
name: String = StdVMTimeResourceSim.DSLNames.name,
unit: String = StdVMTimeResourceSim.DSLNames.unit,
- costPolicy: ChargingBehavior = DiscreteChargingBehavior
-) extends ResourceSim(name, unit, costPolicy) {
+ chargingBehavior: String = classOf[DiscreteChargingBehavior].getName
+) extends ResourceSim(name, unit, chargingBehavior) {
override def newInstance(instanceId: String, owner: UserSim, client: ClientSim) =
StdBandwidthInstanceSim(this, instanceId, owner, client)
class StdDiskspaceResourceSim(
name: String = StdVMTimeResourceSim.DSLNames.name,
unit: String = StdVMTimeResourceSim.DSLNames.unit,
- chargingBehavior: ChargingBehavior = ContinuousChargingBehavior
+ chargingBehavior: String = classOf[ContinuousChargingBehavior].getName
) extends ResourceSim(name, unit, chargingBehavior) {
override def newInstance(instanceId: String, owner: UserSim, client: ClientSim) =
class StdVMTimeResourceSim(
name: String = StdVMTimeResourceSim.DSLNames.name,
unit: String = StdVMTimeResourceSim.DSLNames.unit,
- chargingBehavior: ChargingBehavior = OnOffChargingBehavior
+ chargingBehavior: String = classOf[OnOffChargingBehavior].getName
) extends ResourceSim(name, unit, chargingBehavior) {
override def newInstance(instanceId: String, owner: UserSim, client: ClientSim) =
parentID = policy.parentID,
validityTimespan = policy.validityTimespan,
resourceTypes = policy.resourceTypes,
- chargingBehaviorClasses = policy.chargingBehaviorClasses,
+ chargingBehaviors = policy.chargingBehaviors,
roleMapping = policy.roleMapping
)
_policies = localPolicy :: _policies
case class MongoDBPolicy(
_id: String,
- id: String,
parentID: Option[String],
validityTimespan: Timespan,
resourceTypes: Set[ResourceType],
- chargingBehaviorClasses: Set[String],
+ chargingBehaviors: Set[String],
roleMapping: Map[String/*Role*/, FullPriceTable]
) extends PolicyModel {
+ def id = _id
def idInStore = Some(_id)
}
final def fromOther(policy: PolicyModel, _id: String): MongoDBPolicy = {
MongoDBPolicy(
_id,
- policy.id,
policy.parentID,
policy.validityTimespan,
policy.resourceTypes,
- policy.chargingBehaviorClasses,
+ policy.chargingBehaviors,
policy.roleMapping
)
}
# Relative to AQUARIUM_HOME or an absolute path
# DO NOT set this in production
-#events.store.folder=../events-store
+events.store.folder=../events-store
# Store resource events to events.store.folder as well
events.store.save.rc.events=false
# Override the user event store (if present, it will not be given by the store provider above)
#policy.store.class=
+# A time period in milliseconds for which we can tolerate stale parts regarding user state.
+user.state.timestamp.threshold=1
+
# Administrative REST API authorization cookie
admin.cookie=1
\ No newline at end of file
package gr.grnet.aquarium.logic.test
import org.junit.Test
-import gr.grnet.aquarium.logic.accounting.dsl.{Timeslot, DSLTimeFrameRepeat,DSLTimeFrame}
-import gr.grnet.aquarium.util.TestMethods
+import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
import java.util.Date
import scala._
import scala.Some
package gr.grnet.aquarium.logic.test
-import gr.grnet.aquarium.util.TestMethods
import org.junit.Assert._
import org.junit.{Test}
import gr.grnet.aquarium.logic.accounting.dsl.Timeslot
*
* @author Georgios Gousios <gousiosg@gmail.com>
*/
-class TimeslotTest extends TestMethods {
+class TimeslotTest /*extends TestMethods*/ {
@Test
def testOverlappingTimeslots = {
--- /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.junit.Test
+import gr.grnet.aquarium.Timespan
+import gr.grnet.aquarium.charging.{OnceChargingBehavior, ContinuousChargingBehavior, OnOffChargingBehavior, DiscreteChargingBehavior}
+import gr.grnet.aquarium.converter.{StdConverters, PrettyJsonTextFormat}
+
+/**
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+class StdPolicyTest {
+ final lazy val policy = StdPolicy(
+ id = "policy-1",
+ 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)
+ ),
+
+ chargingBehaviors = Set(
+ classOf[DiscreteChargingBehavior].getName,
+ classOf[OnOffChargingBehavior].getName,
+ classOf[ContinuousChargingBehavior].getName,
+ classOf[OnceChargingBehavior].getName
+ ),
+
+ 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)
+ ))
+ )
+ )
+
+ @Test
+ def testJson(): Unit = {
+ val converters = StdConverters.AllConverters
+ val json = converters.convertEx[PrettyJsonTextFormat](policy)
+ val obj = converters.convertEx[StdPolicy](json)
+
+ assert(policy == obj)
+ }
+}
package gr.grnet.aquarium.user
import gr.grnet.aquarium.store.memory.MemStoreProvider
-import gr.grnet.aquarium.logic.accounting.dsl._
import gr.grnet.aquarium.util.{Loggable, ContextualLogger}
import gr.grnet.aquarium.simulation._
import gr.grnet.aquarium.uid.{UIDGenerator, ConcurrentVMLocalUIDGenerator}
import org.junit.{Assert, Ignore, Test}
import gr.grnet.aquarium.logic.accounting.algorithm.{ExecutableChargingBehaviorAlgorithm, CostPolicyAlgorithmCompiler}
-import gr.grnet.aquarium.{Timespan, Aquarium, ResourceLocator, AquariumBuilder, AquariumException}
+import gr.grnet.aquarium.{Aquarium, ResourceLocator, AquariumBuilder, AquariumException}
import gr.grnet.aquarium.computation.reason.{NoSpecificChangeReason, MonthlyBillingCalculation}
import gr.grnet.aquarium.util.date.MutableDateCalc
import gr.grnet.aquarium.computation.BillingMonthInfo
-import gr.grnet.aquarium.computation.state.{UserStateBootstrap, UserState}
+import gr.grnet.aquarium.computation.state.UserState
import gr.grnet.aquarium.charging._
import gr.grnet.aquarium.policy.{PolicyDefinedFullPriceTableRef, StdUserAgreement, EffectiveUnitPrice, EffectivePriceTable, FullPriceTable, ResourceType, StdPolicy, PolicyModel}
import gr.grnet.aquarium.Timespan
-import scala.Some
import gr.grnet.aquarium.computation.state.UserStateBootstrap
-import gr.grnet.aquarium.simulation.AquariumSim
-import gr.grnet.aquarium.simulation.ClientSim
/**
parentID = None,
validityTimespan = Timespan(0),
resourceTypes = Set(
- ResourceType("bandwidth", "MB/Hr", DiscreteChargingBehavior),
- ResourceType("vmtime", "Hr", OnOffChargingBehavior),
- ResourceType("diskspace", "MB/Hr", ContinuousChargingBehavior)
+ ResourceType("bandwidth", "MB/Hr", classOf[DiscreteChargingBehavior].getName),
+ ResourceType("vmtime", "Hr", classOf[OnOffChargingBehavior].getName),
+ ResourceType("diskspace", "MB/Hr", classOf[ContinuousChargingBehavior].getName)
),
- chargingBehaviorClasses = Set(
- DiscreteChargingBehavior.getClass.getName,
- OnOffChargingBehavior.getClass.getName,
- ContinuousChargingBehavior.getClass.getName
+ chargingBehaviors = Set(
+ classOf[DiscreteChargingBehavior].getName,
+ classOf[OnOffChargingBehavior].getName,
+ classOf[ContinuousChargingBehavior].getName,
+ classOf[OnceChargingBehavior].getName
),
roleMapping = Map(
"default" -> FullPriceTable(Map(
def apply(vars: Map[ChargingInput, Any]): Double = {
vars.apply(ChargingBehaviorNameInput) match {
- case ChargingBehaviorNames.continuous ⇒
+ case ChargingBehaviorAliases.continuous ⇒
val unitPrice = vars(UnitPriceInput).asInstanceOf[Double]
val oldTotalAmount = vars(OldTotalAmountInput).asInstanceOf[Double]
val timeDelta = vars(TimeDeltaInput).asInstanceOf[Double]
creditsForContinuous(timeDelta, oldTotalAmount)
- case ChargingBehaviorNames.discrete ⇒
+ case ChargingBehaviorAliases.discrete ⇒
val unitPrice = vars(UnitPriceInput).asInstanceOf[Double]
val currentValue = vars(CurrentValueInput).asInstanceOf[Double]
creditsForDiscrete(currentValue)
- case ChargingBehaviorNames.onoff ⇒
+ case ChargingBehaviorAliases.onoff ⇒
val unitPrice = vars(UnitPriceInput).asInstanceOf[Double]
val timeDelta = vars(TimeDeltaInput).asInstanceOf[Double]
creditsForOnOff(timeDelta)
- case ChargingBehaviorNames.once ⇒
+ case ChargingBehaviorAliases.once ⇒
val currentValue = vars(CurrentValueInput).asInstanceOf[Double]
currentValue
override def toString = "DefaultAlgorithm(%s)".format(
Map(
- ChargingBehaviorNames.continuous -> "hrs(timeDelta) * oldTotalAmount * %s".format(ContinuousUnitPrice),
- ChargingBehaviorNames.discrete -> "currentValue * %s".format(DiscreteUnitPrice),
- ChargingBehaviorNames.onoff -> "hrs(timeDelta) * %s".format(OnOffUnitPrice),
- ChargingBehaviorNames.once -> "currentValue"))
+ ChargingBehaviorAliases.continuous -> "hrs(timeDelta) * oldTotalAmount * %s".format(ContinuousUnitPrice),
+ ChargingBehaviorAliases.discrete -> "currentValue * %s".format(DiscreteUnitPrice),
+ ChargingBehaviorAliases.onoff -> "hrs(timeDelta) * %s".format(OnOffUnitPrice),
+ ChargingBehaviorAliases.once -> "currentValue"))
}
val DefaultCompiler = new CostPolicyAlgorithmCompiler {