2 * Copyright 2011-2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
12 * 2. Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following
14 * disclaimer in the documentation and/or other materials
15 * provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
24 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
30 * The views and conclusions contained in the software and
31 * documentation are those of the authors and should not be
32 * interpreted as representing official policies, either expressed
33 * or implied, of GRNET S.A.
36 package gr.grnet.aquarium.charging
38 import VMChargingBehavior.SelectorLabels.PowerStatus
39 import VMChargingBehavior.Selectors.Power
40 import gr.grnet.aquarium.charging.state.UserAgreementHistoryModel
41 import gr.grnet.aquarium.computation.BillingMonthInfo
42 import gr.grnet.aquarium.event.DetailsModel
43 import gr.grnet.aquarium.message.MessageConstants
44 import gr.grnet.aquarium.message.avro.gen.{UserStateMsg, WalletEntryMsg, ResourceTypeMsg, ResourcesChargingStateMsg, ResourceInstanceChargingStateMsg, ResourceEventMsg}
45 import gr.grnet.aquarium.message.avro.{AvroHelpers, MessageHelpers, MessageFactory}
46 import gr.grnet.aquarium.util.LogHelpers._
47 import gr.grnet.aquarium.{Aquarium, AquariumInternalError}
48 import scala.collection.JavaConverters.asScalaBufferConverter
49 import gr.grnet.aquarium.Real
50 import gr.grnet.aquarium.HrsOfMillis
53 * The new [[gr.grnet.aquarium.charging.ChargingBehavior]] for VMs usage.
55 * @author Christos KK Loverdos <loverdos@gmail.com>
57 final class VMChargingBehavior extends ChargingBehaviorSkeleton(List(PowerStatus)) {
58 def computeCreditsToSubtract(
59 resourceInstanceChargingState: ResourceInstanceChargingStateMsg,
61 timeDeltaMillis: Long,
63 ): (Real, String /* explanation */) = {
65 val credits = HrsOfMillis(timeDeltaMillis) * unitPrice
66 val explanation = "Hours(%s) * UnitPrice(%s)".format(HrsOfMillis(timeDeltaMillis), unitPrice)
68 (credits, explanation)
72 def computeSelectorPath(
73 chargingBehaviorDetails: DetailsModel.Type,
74 resourceInstanceChargingState: ResourceInstanceChargingStateMsg,
75 currentResourceEvent: ResourceEventMsg,
76 referenceFromMillis: Long,
77 referenceToMillis: Long,
80 val previousEvents = resourceInstanceChargingState.getPreviousEvents.asScala.toList
81 (currentResourceEvent.getValue.toInt,previousEvents) match {
82 case (1,Nil) => // create --> on
85 case (x,hd::_) => (x,hd.getValue.toInt) match {
86 case (1,0) => // off ---> on
88 case (0,1) => // on ---> off
90 case (2,1) => // off --> destroy
91 //List(Power.powerOff,Power.destroy)
94 throw new AquariumInternalError("Invalid state") // FIXME better message
97 throw new AquariumInternalError("Invalid state") // FIXME better message
101 def initialChargingDetails = {
105 def computeNewAccumulatingAmount(
106 resourceInstanceChargingState: ResourceInstanceChargingStateMsg,
107 eventDetails: DetailsModel.Type
109 Real(resourceInstanceChargingState.getCurrentValue)
112 def constructImplicitEndEventFor(resourceEvent: ResourceEventMsg, newOccurredMillis: Long) = {
113 assert(VMChargingBehaviorValues.isONValue(resourceEvent.getValue))
115 val details = resourceEvent.getDetails
116 val newDetails = DetailsModel.copyOf(details)
117 MessageHelpers.setAquariumSyntheticAndImplicitEnd(newDetails)
119 ResourceEventMsg.newBuilder(resourceEvent).
120 setDetails(newDetails).
121 setOccurredMillis(newOccurredMillis).
122 setReceivedMillis(newOccurredMillis).
123 setValue(VMChargingBehaviorValues.OFF).
127 def constructImplicitStartEventFor(resourceEvent: ResourceEventMsg) = {
128 throw new AquariumInternalError("constructImplicitStartEventFor() Not compliant with %s", this)
133 * @return The number of wallet entries recorded and the new total credits
135 override def processResourceEvent(
137 resourceEvent: ResourceEventMsg,
138 resourceType: ResourceTypeMsg,
139 billingMonthInfo: BillingMonthInfo,
140 resourcesChargingState: ResourcesChargingStateMsg,
141 userAgreementHistoryModel: UserAgreementHistoryModel,
142 userStateMsg: UserStateMsg,
143 walletEntryRecorder: WalletEntryMsg ⇒ Unit
146 // 1. Ensure proper initial state per resource and per instance
147 ensureInitializedWorkingState(resourcesChargingState,resourceEvent)
149 // 2. Fill in data from the new event
150 val stateOfResourceInstance = resourcesChargingState.getStateOfResourceInstance
151 val resourcesChargingStateDetails = resourcesChargingState.getDetails
152 val instanceID = resourceEvent.getInstanceID
153 val resourceInstanceChargingState = stateOfResourceInstance.get(instanceID)
154 fillWorkingResourceInstanceChargingStateFromEvent(resourceInstanceChargingState, resourceEvent)
156 val previousEvents = resourceInstanceChargingState.getPreviousEvents
157 val retVal = previousEvents.size() match {
162 val previousEvent = previousEvents.get(0) // head is most recent
163 Debug(logger, "I have previous event %s", AvroHelpers.jsonStringOfSpecificRecord(previousEvent))
165 computeWalletEntriesForNewEvent(
169 Real(userStateMsg.getTotalCredits),
170 previousEvent.getOccurredMillis,
171 resourceEvent.getOccurredMillis,
172 userAgreementHistoryModel.agreementByTimeslot,
173 resourcesChargingStateDetails,
174 resourceInstanceChargingState,
180 // We need just one previous event, so we update it
181 MessageHelpers.setOnePreviousEvent(resourceInstanceChargingState, resourceEvent)
185 def createVirtualEventsForRealtimeComputation(
187 resourceTypeName: String,
188 resourceInstanceID: String,
189 eventOccurredMillis: Long,
190 resourceInstanceChargingState: ResourceInstanceChargingStateMsg
191 ): List[ResourceEventMsg] = {
192 val resourceInstanceID = resourceInstanceChargingState.getInstanceID
194 def vmEvent(value: String) : List[ResourceEventMsg] = {
195 val dm = DetailsModel.make
196 DetailsModel.setBoolean(dm, MessageConstants.DetailsKeys.aquarium_is_synthetic)
197 DetailsModel.setBoolean(dm, MessageConstants.DetailsKeys.aquarium_is_realtime_virtual)
199 MessageFactory.newResourceEventMsg(
200 MessageHelpers.VirtualEventsIDGen.nextUID(),
208 MessageConstants.EventVersion_1_0,
213 def mkON = vmEvent(VMChargingBehaviorValues.ON)
214 def mkOFF = vmEvent(VMChargingBehaviorValues.OFF)
216 val previousEvents = resourceInstanceChargingState.getPreviousEvents
217 previousEvents.size() match {
221 previousEvents.get(0).getValue.toInt match {
222 case 0 ⇒ mkON //produce an on event
223 case 1 ⇒ mkOFF //produce an off event
224 case 2 ⇒ mkOFF //produce an off event
230 object VMChargingBehavior {
231 object SelectorLabels {
232 final val PowerStatus = "Power Status (ON/OFF)"
237 // When the VM is created
238 //final val create = "create"
240 // When the VM is destroyed
241 //final val destroy = "destroy"
243 // When the VM is powered on
244 final val powerOn = "powerOn"
246 // When the VM is powered off
247 final val powerOff = "powerOff"