4b72562e038f964df1b7cac7deb6125b5a7a3e7a
[aquarium] / src / main / scala / gr / grnet / aquarium / event / model / resource / ResourceEventModel.scala
1 /*
2  * Copyright 2011-2012 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.event.model
37 package resource
38
39 import gr.grnet.aquarium.event.model.ExternalEventModel
40 import gr.grnet.aquarium.util.date.MutableDateCalc
41 import java.util.Date
42
43 /**
44  * The model of any resource event.
45  *
46  * @author Christos KK Loverdos <loverdos@gmail.com>
47  */
48
49 trait ResourceEventModel extends ExternalEventModel {
50   /**
51    * Identifies the client that sent this event.
52    */
53   def clientID: String
54
55   /**
56    * String representation of the resource type (e.g. "bndup", "vmtime").
57    */
58   def resource: String
59
60   /**
61    * String representation of the resource instance id
62    */
63   def instanceID: String
64
65
66   /**
67    * The resource value.
68    */
69   def value: Double
70
71   def withDetails(newDetails: Map[String, String], newOccurredMillis: Long): ResourceEventModel
72
73   def withDetailsAndValue(newDetails: Map[String, String], newValue: Double, newOccurredMillis: Long): ResourceEventModel
74
75   def safeResource   = if(resource eq null)   "" else resource
76   def safeInstanceID = if(instanceID eq null) "" else instanceID
77
78   def safeResourceInstanceInfo = (safeResource, safeInstanceID)
79
80   def occurredDate = new Date(occurredMillis)
81
82   def isOccurredWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
83     require(fromMillis <= toMillis, "fromMillis <= toMillis")
84     fromMillis <= occurredMillis && occurredMillis <= toMillis
85   }
86
87   def isReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
88     require(fromMillis <= toMillis, "fromMillis <= toMillis")
89     fromMillis <= receivedMillis && receivedMillis <= toMillis
90   }
91
92   def isOccurredOrReceivedWithinMillis(fromMillis: Long, toMillis: Long): Boolean = {
93     isOccurredWithinMillis(fromMillis, toMillis) ||
94     isReceivedWithinMillis(fromMillis, toMillis)
95   }
96
97   def isOutOfSyncForBillingMonth(yearOfBillingMonth: Int, billingMonth: Int) = {
98     val billingStartDateCalc = new MutableDateCalc(yearOfBillingMonth, billingMonth)
99     val billingStartMillis = billingStartDateCalc.toMillis
100     // NOTE: no need to `copy` the mutable `billingStartDateCalc` here because we use it once
101     val billingStopMillis  = billingStartDateCalc.goEndOfThisMonth.toMillis
102
103     isOutOfSyncForBillingPeriod(billingStartMillis, billingStopMillis)
104   }
105
106   def isOutOfSyncForBillingPeriod(billingStartMillis: Long, billingStopMillis: Long): Boolean = {
107     // Out of sync events are those that were received within the billing period
108     // but actually occurred outside the billing period.
109      isReceivedWithinMillis(billingStartMillis, billingStopMillis) &&
110     !isOccurredWithinMillis(billingStartMillis, billingStopMillis)
111   }
112
113   override def toDebugString: String = {
114     val instanceInfo = "%s::%s".format(resource, instanceID)
115     val occurredFormatted = new MutableDateCalc(occurredMillis).toYYYYMMDDHHMMSS
116     if(occurredMillis == receivedMillis) {
117       "%sEVENT(%s, [%s], %s, %s, %s, %s, %s)".format(
118         if(isSynthetic) "*" else "",
119         id,
120         occurredFormatted,
121         value,
122         instanceInfo,
123         details,
124         userID,
125         clientID
126       )
127     } else {
128       "%sEVENT(%s, [%s], [%s], %s, %s, %s, %s, %s)".format(
129         if(isSynthetic) "*" else "",
130         id,
131         occurredFormatted,
132         new MutableDateCalc(receivedMillis),
133         value,
134         instanceInfo,
135         details,
136         userID,
137         clientID
138       )
139     }
140   }
141
142   /**
143    * `Synthetic` means that Aquarium has manufactured this resource event for some purpose. For example, the implicitly
144    * issued resource events at the end a a billing period.
145    *
146    * @return `true` iff this resource event is synthetic.
147    */
148   def isSynthetic = {
149     details contains ResourceEventModel.Names.details_aquarium_is_synthetic
150   }
151 }
152
153 object ResourceEventModel {
154   /**
155    * A `ResourceInstance` is the type of a specific resource instance and it is contains the name of the
156    * [[gr.grnet.aquarium.policy.ResourceType]] and the instance `ID`.
157    */
158   type ResourceInstance = (String, String)
159   type FullResourceTypeMap = Map[ResourceInstance, ResourceEventModel]
160   type FullMutableResourceTypeMap = scala.collection.mutable.Map[ResourceInstance, ResourceEventModel]
161
162   trait NamesT extends ExternalEventModel.NamesT {
163     final val clientID = "clientID"
164     final val resource = "resource"
165     final val instanceID = "instanceID"
166     final val value = "value"
167
168     // This is set in the details map to indicate a synthetic resource event (ie not a real one).
169     // Examples of synthetic resource events are those that are implicitly generated at the
170     // end of the billing period (e.g. `OFF`s).
171     final val details_aquarium_is_synthetic    = "__aquarium_is_synthetic__"
172
173     final val details_aquarium_is_implicit_end = "__aquarium_is_implicit_end__"
174
175     final val details_aquarium_is_dummy_first = "__aquarium_is_dummy_first__"
176
177     final val details_aquarium_is_realtime_virtual = "__aquarium_is_realtime_virtual__"
178
179     final val details_aquarium_reference_event_id = "__aquarium_reference_event_id__"
180
181     final val details_aquarium_reference_event_id_in_store = "__aquarium_reference_event_id_in_store__"
182   }
183
184   object Names extends NamesT
185
186   def setAquariumSyntheticAndImplicitEnd(map: Map[String, String]): Map[String, String] = {
187     map.
188       updated(Names.details_aquarium_is_synthetic, "true").
189       updated(Names.details_aquarium_is_implicit_end, "true")
190   }
191
192   def setAquariumSyntheticAndDummyFirst(map: Map[String, String]): Map[String, String] = {
193     map.
194       updated(Names.details_aquarium_is_synthetic, "true").
195       updated(Names.details_aquarium_is_dummy_first, "true")
196   }
197 }