Augment and use resources map functionality
[aquarium] / src / main / scala / gr / grnet / aquarium / logic / events / ResourceEvent.scala
1 /*
2  * Copyright 2011 GRNET S.A. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or
5  * without modification, are permitted provided that the following
6  * conditions are met:
7  *
8  *   1. Redistributions of source code must retain the above
9  *      copyright notice, this list of conditions and the following
10  *      disclaimer.
11  *
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.
16  *
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.
29  *
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.
34  */
35
36 package gr.grnet.aquarium.logic.events
37
38 import net.liftweb.json.{JsonAST, Xml}
39 import gr.grnet.aquarium.util.json.JsonHelpers
40 import gr.grnet.aquarium.logic.accounting.dsl._
41 import com.ckkloverdos.maybe.Maybe
42
43 /**
44  * Event sent to Aquarium by clients for resource accounting.
45  * 
46  * @author Christos KK Loverdos <loverdos@gmail.com>.
47  * @author Georgios Gousios <gousiosg@gmail.com>.
48  */
49 case class ResourceEvent(
50     override val id: String,           // The id at the client side (the sender) TODO: Rename to remoteId or something...
51     override val occurredMillis: Long, // When it occurred at client side (the sender)
52     override val receivedMillis: Long, // When it was received by Aquarium
53     userId: String,                    // The user for which this resource is relevant
54     clientId: String,                  // The unique client identifier (usually some hash)
55     resource: String,                  // String representation of the resource type (e.g. "bndup", "vmtime").
56     instanceId: String,                // String representation of the resource instance id
57     eventVersion: String,
58     value: Double,
59     details: ResourceEvent.Details)
60   extends AquariumEvent(id, occurredMillis, receivedMillis) {
61
62   def validate() : Boolean = {
63     !safeResource.isEmpty
64   }
65
66   def safeResource   = if(resource eq null)   "" else resource
67   def safeInstanceId = if(instanceId eq null) "" else instanceId
68
69   def hasResource   = !safeResource.isEmpty
70   def hasInstanceId = !safeInstanceId.isEmpty
71
72   def fullResourceInfo = (safeResource, safeInstanceId)
73
74   /**
75    * Returns a beautiful string representation of the value.
76    *
77    * @param policy The policy to be asked for resources.
78    * @return A beautiful string representation of the value.
79    */
80   def beautifyValue(policy: DSLPolicy): String = {
81     policy.findResource(this.resource) match {
82       case Some(DSLComplexResource(_, _, OnOffCostPolicy, _)) ⇒
83         OnOffPolicyResourceState(this.value).state.toUpperCase
84       case Some(DSLSimpleResource(_, _, OnOffCostPolicy)) ⇒
85         OnOffPolicyResourceState(this.value).state.toUpperCase
86       case _ ⇒
87         value.toString
88     }
89   }
90
91   /**
92    * Returns a beautiful string representation of the value.
93    *
94    * @param resourcesMap The resources map to be asked for resources.
95    * @return A beautiful string representation of the value.
96    */
97   def beautifyValue(resourcesMap: DSLResourcesMap): String = {
98     resourcesMap.findResource(this.resource) match {
99       case Some(DSLComplexResource(_, _, OnOffCostPolicy, _)) ⇒
100         OnOffPolicyResourceState(this.value).state.toUpperCase
101       case Some(DSLSimpleResource(_, _, OnOffCostPolicy)) ⇒
102         OnOffPolicyResourceState(this.value).state.toUpperCase
103       case _ ⇒
104         value.toString
105     }
106   }
107
108   /**
109    * Return `true` iff this is an event regarding a resource with an
110    * [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]].
111    */
112   def isOnOffEvent(policy: DSLPolicy): Boolean = {
113     policy.findResource(this.resource).map(_.costPolicy) match {
114       case Some(OnOffCostPolicy) ⇒ true
115       case _ ⇒ false
116     }
117   }
118
119   /**
120    * Return `true` iff this is an event regarding a resource with an
121    * [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]] and a
122    * `value` of `"on"`.
123    */
124   def isOnEvent(policy: DSLPolicy): Boolean = {
125     policy.findResource(this.resource) match {
126       case Some(DSLComplexResource(_, _, OnOffCostPolicy, _)) ⇒
127         OnOffPolicyResourceState(this.value).isOn
128       case Some(DSLSimpleResource(_, _, OnOffCostPolicy)) ⇒
129         OnOffPolicyResourceState(this.value).isOn
130       case _ ⇒
131         false
132     }
133   }
134
135   /**
136    * Return `true` iff this is an event regarding a resource with an
137    * [[gr.grnet.aquarium.logic.accounting.dsl.OnOffCostPolicy]] and a
138    * `value` of `"off"`.
139    */
140   def isOffEvent(policy: DSLPolicy): Boolean = {
141     policy.findResource(this.resource) match {
142       case Some(DSLComplexResource(_, _, OnOffCostPolicy, _)) ⇒
143         OnOffPolicyResourceState(this.value).isOff
144       case Some(DSLSimpleResource(_, _, OnOffCostPolicy)) ⇒
145         OnOffPolicyResourceState(this.value).isOff
146       case _ ⇒
147         false
148     }
149   }
150
151   def copyWithReceivedMillis(millis: Long) = copy(receivedMillis = millis)
152
153   /**
154    * Find the cost policy of the resource named in this resource event.
155    *
156    * We do not expect cost policies for resources to change, because they are supposed
157    * to be one of their constant characteristics. That is why do not issue a time-dependent
158    * query here for the event's current policy.
159    *
160    * Should the need arises to change the cost policy for a resource, this is a good enough
161    * reason to consider creating another type of resource.
162    */
163   def findCostPolicy(defaultPolicy: DSLPolicy): Maybe[DSLCostPolicy] = {
164     defaultPolicy.findResource(this.safeResource).map(_.costPolicy): Maybe[DSLCostPolicy]
165   }
166 }
167
168 object ResourceEvent {
169   type Details = Map[String, String]
170
171   type ResourceType = String
172   type ResourceIdType = String
173   type FullResourceType = (ResourceType, ResourceIdType)
174   
175   def fromJson(json: String): ResourceEvent = {
176     JsonHelpers.jsonToObject[ResourceEvent](json)
177   }
178
179   def fromJValue(jsonAST: JsonAST.JValue): ResourceEvent = {
180     JsonHelpers.jValueToObject[ResourceEvent](jsonAST)
181   }
182
183   def fromBytes(bytes: Array[Byte]): ResourceEvent = {
184     JsonHelpers.jsonBytesToObject[ResourceEvent](bytes)
185   }
186
187   def fromXml(xml: String): ResourceEvent = {
188     fromJValue(Xml.toJson(scala.xml.XML.loadString(xml)))
189   }
190
191   object JsonNames {
192     final val _id = "_id"
193     final val id = "id"
194     final val userId = "userId"
195     //final val timestamp = "timestamp" // TODO: deprecate in favor of "occurredMillis"
196     final val occurredMillis = "occurredMillis"
197     final val receivedMillis = "receivedMillis"
198     final val clientId = "clientId"
199     final val resource = "resource"
200     final val resourceId = "resourceId"
201     final val eventVersion = "eventVersion"
202     final val value = "value"
203     final val details = "details"
204
205     // ResourceType: VMTime
206     final val vmId = "vmId"
207     final val action = "action" // "on", "off"
208   }
209 }