Update available policies when on configuration file update
[aquarium] / src / main / scala / gr / grnet / aquarium / logic / accounting / Policy.scala
index a43de05..69eb007 100644 (file)
 
 package gr.grnet.aquarium.logic.accounting
 
+import dsl.{Timeslot, DSLPolicy, DSL}
 import gr.grnet.aquarium.Configurator._
-import dsl.DSL
-import com.ckkloverdos.maybe.{NoVal, Failed, Just}
-import gr.grnet.aquarium.util.Loggable
 import java.io.{InputStream, FileInputStream, File}
+import java.util.Date
+import com.ckkloverdos.maybe.{Maybe, Just}
+import gr.grnet.aquarium.util.date.TimeHelpers
+import gr.grnet.aquarium.logic.events.PolicyEntry
+import gr.grnet.aquarium.util.{CryptoUtils, Loggable}
 
 /**
  * Searches for and loads the applicable accounting policy
@@ -47,33 +50,97 @@ import java.io.{InputStream, FileInputStream, File}
  * @author Georgios Gousios <gousiosg@gmail.com>
  */
 object Policy extends DSL with Loggable {
-  lazy val policy = {
+  
+  private var policies = {reloadPolicies}
+  
+  lazy val policy = loadPolicyFromFile(policyFile)
 
-    // Look for user configured policy first
-    val userConf = MasterConfigurator.props.get(Keys.aquarium_policy) match {
-      case Just(x) => x
-      case _ => logger.info("Cannot find a user configured policy")
-        "policy.yaml"
+  def policy(at: Date): Maybe[DSLPolicy] = Maybe {
+    policies.find {
+      a => a._1.from.before(at) &&
+           a._1.to.after(at)
+    } match {
+      case Some(x) => x._2
+      case None =>
+        throw new AccountingException("No valid policy for date: %s".format(at))
     }
+  }
+
+  def policies(from: Date, to: Date): List[DSLPolicy] = {
+    policies.filter {
+      a => a._1.from.before(from) &&
+           a._1.to.after(to)
+    }.values.toList
+  }
+  
+  def policies(t: Timeslot): List[DSLPolicy] = policies(t.from, t.to)
+
+  def loadPolicyFromFile(pol: File): DSLPolicy = {
 
-    val pol = new File(userConf)
     val stream = pol.exists() match {
       case true =>
-        logger.info("Using policy file %s".format(userConf))
+        logger.info("Using policy file %s".format(pol.getAbsolutePath))
         new FileInputStream(pol)
       case false =>
-        logger.warn(("Cannot find policy file %s, " +
-          "looking for default policy").format(userConf))
+        logger.warn(("Cannot find user configured policy file %s, " +
+          "looking for default policy").format(pol.getAbsolutePath))
         getClass.getClassLoader.getResourceAsStream("policy.yaml") match {
           case x: InputStream =>
-            logger.warn("Using default policy, this is problably bad")
+            logger.warn("Using default policy, this is problably not what you want")
             x
           case null =>
             logger.error("No valid policy file found, Aquarium will fail")
             null
         }
     }
-
     parse(stream)
   }
+
+  private def policyFile: File =
+    MasterConfigurator.props.get(Keys.aquarium_policy) match {
+      case Just(x) => new File(x)
+      case _ => new File("/etc/aquarium/policy.yaml")
+    }
+
+  private def reloadPolicies(): Map[Timeslot, DSLPolicy] = synchronized {
+    //1. Load policies from db
+    val policies = MasterConfigurator.policyEventStore.loadPolicies(0)
+
+    //2. Check whether policy file has been updated
+    val latestPolicyChange = if (policies.isEmpty) 0 else policies.last.validFrom
+    val policyf = policyFile
+    var updated = false
+
+    if (policyf.exists) {
+      if (policyf.lastModified > latestPolicyChange) {
+        logger.info("Policy changed since last check, reloading")
+        updated = true
+      } else {
+        logger.info("Policy not changed since last check")
+      }
+    } else {
+      logger.warn("User specified policy file %s does not exist, " +
+        "using stored policy information".format(policyf.getAbsolutePath))
+    }
+
+    val toAdd = updated match {
+      case true =>
+        val ts = TimeHelpers.nowMillis
+        val toUpdate = policies.last.copy(validTo = ts)
+        val parsedNew = loadPolicyFromFile(policyf)
+        val yaml = parsedNew.toYAML
+        val newPolicy = PolicyEntry(CryptoUtils.sha1(yaml), ts, ts, yaml, ts + 1, -1)
+
+        MasterConfigurator.policyEventStore.updatePolicy(toUpdate)
+        MasterConfigurator.policyEventStore.storePolicy(newPolicy)
+
+        List(toUpdate, newPolicy)
+      case false => List()
+    }
+
+    policies.init.++(toAdd).foldLeft(Map[Timeslot, DSLPolicy]()){
+      (acc, p) =>
+        acc ++ Map(Timeslot(new Date(p.validFrom), new Date(p.validTo)) -> parse(p.policyYAML))
+    }
+  }
 }
\ No newline at end of file