Hook selectors into the price computation path selectors
authorChristos KK Loverdos <loverdos@gmail.com>
Thu, 16 Aug 2012 12:58:33 +0000 (15:58 +0300)
committerChristos KK Loverdos <loverdos@gmail.com>
Thu, 16 Aug 2012 12:58:33 +0000 (15:58 +0300)
14 files changed:
pom.xml
src/main/resources/policy.json
src/main/scala/gr/grnet/aquarium/charging/ChargingBehavior.scala
src/main/scala/gr/grnet/aquarium/charging/OnceChargingBehavior.scala
src/main/scala/gr/grnet/aquarium/charging/VMChargingBehavior.scala
src/main/scala/gr/grnet/aquarium/computation/TimeslotComputations.scala
src/main/scala/gr/grnet/aquarium/converter/JsonConversions.scala
src/main/scala/gr/grnet/aquarium/event/model/resource/ResourceEventModel.scala
src/main/scala/gr/grnet/aquarium/policy/UserAgreementModel.scala
src/test/resources/logback-test.xml
src/test/resources/policy.json
src/test/scala/gr/grnet/aquarium/event/model/StdResourceEventTest.scala [new file with mode: 0644]
src/test/scala/gr/grnet/aquarium/policy/StdPolicyTest.scala
src/test/scala/gr/grnet/aquarium/user/UserStateComputationsTest.scala

diff --git a/pom.xml b/pom.xml
index f2cc62c..27c88f6 100644 (file)
--- a/pom.xml
+++ b/pom.xml
       <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>
index 3053ee6..990e2a2 100644 (file)
@@ -1,22 +1,23 @@
-{
-  "id":"policy-1",
+{ "jsonClass":"gr.grnet.aquarium.policy.StdPolicy",
+
+  "id":"default-policy",
+
   "validityTimespan":{
     "fromMillis":0,
     "toMillis":9223372036854775807
   },
 
-  "resourceTypes":[
-    {
-      "name":"vmtime",
-      "unit":"Hr",
-      "chargingBehavior":"gr.grnet.aquarium.charging.VMChargingBehavior"
-    },
-    {
-      "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"
+  }],
 
   "chargingBehaviors":[
     "gr.grnet.aquarium.charging.VMChargingBehavior",
 
   "roleMapping":{
     "default":{
+      "jsonClass":"gr.grnet.aquarium.policy.FullPriceTable",
+
       "perResource":{
-        "vmtime":    { "priceOverrides":[ { "unitPrice":0.01 } ] },
-        "diskspace": { "priceOverrides":[ { "unitPrice":0.01 } ] }
+        "diskspace":{
+          "default":{ "jsonClass":"gr.grnet.aquarium.policy.EffectivePriceTable",
+            "priceOverrides":[{ "jsonClass":"gr.grnet.aquarium.policy.EffectiveUnitPrice",
+              "unitPrice":0.01
+            }]
+          }
+        },
+
+        "vmtime":{
+          "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
+            }]
+          }
+        }
       }
     }
   }
index b0bb219..99e9d39 100644 (file)
@@ -40,7 +40,7 @@ import scala.collection.mutable
 
 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
@@ -177,22 +177,25 @@ abstract class ChargingBehavior(
       referenceTimeslot.to.getTime
     )
 
-    val selectorPath = computeSelectorPath(
-      chargingData,
-      currentResourceEvent,
-      referenceTimeslot,
-      previousValue,
-      totalCredits,
-      _oldAccumulatingAmount,
-      _newAccumulatingAmount
-    )
+    val effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable = fullPriceTable ⇒ {
+      this.selectEffectivePriceTable(
+        fullPriceTable,
+        chargingData,
+        currentResourceEvent,
+        referenceTimeslot,
+        previousValue,
+        totalCredits,
+        _oldAccumulatingAmount,
+        _newAccumulatingAmount
+      )
+    }
 
     val initialChargeslots = TimeslotComputations.computeInitialChargeslots(
       referenceTimeslot,
       resourceType,
       policyByTimeslot,
       agreementByTimeslot,
-      selectorPath
+      effectivePriceTableSelector
     )
 
     val fullChargeslots = initialChargeslots.map {
@@ -268,6 +271,30 @@ abstract class ChargingBehavior(
     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
index a764d1f..bc8327c 100644 (file)
@@ -39,6 +39,7 @@ 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.
@@ -61,7 +62,7 @@ final class OnceChargingBehavior
       oldAccumulatingAmount: Double,
       newAccumulatingAmount: Double
   ): List[String] = {
-    Nil
+    List(FullPriceTable.DefaultSelectorKey)
   }
   /**
    * This is called when we have the very first event for a particular resource instance, and we want to know
index 21fdec1..1fa1237 100644 (file)
@@ -40,6 +40,7 @@ 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}
 
 /**
  * The new [[gr.grnet.aquarium.charging.ChargingBehavior]] for VMs usage.
index e63f7ef..c0da20d 100644 (file)
@@ -91,14 +91,14 @@ object TimeslotComputations extends Loggable {
       policy: PolicyModel,
       agreement: UserAgreementModel,
       resourceType: ResourceType,
-      selectorPath: List[String],
+      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, selectorPath)
+    val ret = resolveEffectiveUnitPricesForTimeslot(alignedTimeslot, policy, agreement, resourceType, effectivePriceTableSelector)
     ret map {case (t,p) => (t,p.unitPrice)}
   }
 
@@ -107,7 +107,7 @@ object TimeslotComputations extends Loggable {
       resourceType: ResourceType,
       policyByTimeslot: SortedMap[Timeslot, PolicyModel],
       agreementByTimeslot: SortedMap[Timeslot, UserAgreementModel],
-      selectorPath: List[String],
+      effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable,
       clogOpt: Option[ContextualLogger] = None
   ): List[Chargeslot] = {
 
@@ -145,7 +145,7 @@ object TimeslotComputations extends Loggable {
         policy,
         userAgreement,
         resourceType,
-        selectorPath,
+        effectivePriceTableSelector,
         Some(clog)
       )
 
@@ -217,19 +217,16 @@ object TimeslotComputations extends Loggable {
      * Resolves the effective price list for each chunk of the
      * provided timeslot and returns it as a Map
      */
-    private def resolveEffectiveUnitPricesForTimeslot(
+    private[this] def resolveEffectiveUnitPricesForTimeslot(
         alignedTimeslot: Timeslot,
         policy: PolicyModel,
         agreement: UserAgreementModel,
         resourceType: ResourceType,
-        selectorPath: List[String]
+        effectivePriceTableSelector: FullPriceTable ⇒ EffectivePriceTable
     ): PriceMap = {
 
-      val effectivePriceTable = agreement.effectivePriceTableOfResourceTypeForSelector(
-        resourceType.name,
-        selectorPath,
-        policy
-      )
+      val fullPriceTable = agreement.computeFullPriceTable(policy)
+      val effectivePriceTable = effectivePriceTableSelector(fullPriceTable)
 
       resolveEffective(alignedTimeslot, effectivePriceTable.priceOverrides)
       //immutable.SortedMap(alignedTimeslot -> effectivePriceTable.priceOverrides.head)
index 2922e74..e4204f6 100644 (file)
@@ -41,7 +41,8 @@ import ext.JodaTimeSerializers
 
 import gr.grnet.aquarium.util.{makeString, UTF_8_Charset}
 import java.nio.charset.Charset
-import gr.grnet.aquarium.policy.PolicyModel
+import gr.grnet.aquarium.policy.{ResourceType, EffectiveUnitPrice, EffectivePriceTable, FullPriceTable, StdPolicy}
+import gr.grnet.aquarium.charging.state.WorkingUserState
 
 /**
  * Provides conversion methods from and to JSON.
@@ -56,7 +57,22 @@ object JsonConversions {
    * The application-wide JSON formats used from the underlying lift-json library.
    */
 //  implicit final val Formats = (DefaultFormats ++ JodaTimeSerializers.all)
-  implicit final val Formats = (DefaultFormats.withHints(FullTypeHints(List(classOf[AnyRef]))) ++ 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] = {
index 9617ca1..78797d6 100644 (file)
@@ -39,6 +39,8 @@ package resource
 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.
index 7e3c436..d540ff5 100644 (file)
@@ -36,6 +36,7 @@
 package gr.grnet.aquarium.policy
 
 import gr.grnet.aquarium.{AquariumInternalError, Timespan}
+import gr.grnet.aquarium.charging.ChargingBehavior
 
 /**
  *
@@ -75,13 +76,8 @@ trait UserAgreementModel extends Ordered[UserAgreementModel] {
     }
   }
 
-  def effectivePriceTableOfResourceTypeForSelector(
-      resource: String,
-      selectorPath: List[String],
-      policy: PolicyModel
-  ): EffectivePriceTable = {
-
-    val fullPriceTable = this.fullPriceTableRef match {
+  def computeFullPriceTable(policy: PolicyModel): FullPriceTable = {
+    this.fullPriceTableRef match {
       case PolicyDefinedFullPriceTableRef ⇒
         policy.roleMapping.get(role) match {
           case Some(fullPriceTable) ⇒
@@ -94,7 +90,5 @@ trait UserAgreementModel extends Ordered[UserAgreementModel] {
       case AdHocFullPriceTableRef(fullPriceTable) ⇒
         fullPriceTable
     }
-
-    fullPriceTable.effectivePriceTableOfSelectorForResource(selectorPath, resource)
   }
 }
index 5652f5e..1ec66a2 100644 (file)
@@ -8,7 +8,7 @@
 
   <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"/>
 
index b83add8..5c2ded8 100644 (file)
@@ -1,59 +1,54 @@
-{
-  "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.VMChargingBehavior"
-    },
-    {
-      "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"
+  }],
 
   "chargingBehaviors":[
-    "gr.grnet.aquarium.charging.DiscreteChargingBehavior",
     "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":[
-            {
-              "unitPrice":0.01
-            }
-          ]
-        },
-        "diskspace":{
-          "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
+            }]
+          }
         }
       }
     }
diff --git a/src/test/scala/gr/grnet/aquarium/event/model/StdResourceEventTest.scala b/src/test/scala/gr/grnet/aquarium/event/model/StdResourceEventTest.scala
new file mode 100644 (file)
index 0000000..75dd10d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.event.model
+
+import org.junit.Test
+import gr.grnet.aquarium.event.model.resource.StdResourceEvent
+
+/**
+ *
+ * @author Christos KK Loverdos <loverdos@gmail.com>
+ */
+
+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)
+
+    assert(rc == obj)
+  }
+}
index d1aad35..3dc1160 100644 (file)
@@ -49,7 +49,7 @@ import FullPriceTable.DefaultSelectorKey
 
 class StdPolicyTest {
   final lazy val policy = StdPolicy(
-    id = "policy-1",
+    id = "default-policy",
     parentID = None,
 
     validityTimespan = Timespan(0),
@@ -81,10 +81,10 @@ class StdPolicyTest {
   @Test
   def testJson(): Unit = {
     val json = policy.toJsonString
-    println(json)
-
     val obj = PolicyModel.fromJsonString(json)
 
+    println(json)
+
     assert(policy == obj)
   }
 }
index ab1713b..da4c635 100644 (file)
@@ -91,7 +91,6 @@ class UserStateComputationsTest extends Loggable {
     ),
     roleMapping = Map(
       "default" -> FullPriceTable(Map(
-        "bandwidth" -> Map(DefaultSelectorKey -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil)),
         "diskspace" -> Map(DefaultSelectorKey -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil)),
         "vmtime"    -> Map(DefaultSelectorKey -> EffectivePriceTable(EffectiveUnitPrice(0.01) :: Nil))
       ))