<commit_before>(ns vsphere.vijava
  (:use 
    [slingshot.slingshot :only  [throw+ try+]]
    [clojure.core.strint :only  (<<)]
    [celestial.common :only (get! import-logging)])
  (:import 
    java.net.URL
    com.vmware.vim25.mo.InventoryNavigator
    com.vmware.vim25.VirtualMachineRelocateTransformation
    com.vmware.vim25.mo.ServiceInstance
    com.vmware.vim25.VirtualMachineCloneSpec
    com.vmware.vim25.VirtualMachineConfigSpec
    com.vmware.vim25.VirtualMachineRelocateSpec
    com.vmware.vim25.mo.Folder
    com.vmware.vim25.VirtualMachinePowerState
    com.vmware.vim25.mo.InventoryNavigator
    com.vmware.vim25.mo.ServiceInstance
    com.vmware.vim25.mo.Task
    com.vmware.vim25.mo.VirtualMachine 
    java.lang.Throwable 
    )
  )

(import-logging)

(def ^:dynamic service)

(defn connect [{:keys [url username password]}]
  (ServiceInstance. (URL. url) username password true))

(def services 
  (memoize 
    (fn []  
      (into [] 
        (repeatedly (get! :hypervisor :vsphere :session-count) #(agent (connect (get! :hypervisor :vsphere))))))))

(defn session-expired? [instance]
  (nil? (:currentSession (bean (.getSessionManager (deref (first (services))))))))

(defn renew-session [s]
  (if (session-expired? s)
    (do 
      (debug "Session expired renewing")
      (connect (get! :hypervisor :vsphere)))
    s))

(defmacro execute [body p]
  `(fn [s#]
     (binding [service (renew-session s#)]
       (try 
         (deliver ~p ~body) service) 
       (catch Throwable e# (deliver ~p e#) service))))

(defmacro with-service 
  "Uses recycled service instances see http://bit.ly/YRsiNo, 
  We try to keep all agents busy still rand isn't fair (cycle would work better)."
  [body]
  `(let [a# ((services) (rand-int (count (services)))) p# (promise)]
     (send a# (execute ~body p#)) 
     (let [res# @p#]
       (when (instance? Throwable res#)
         (throw res#))
       res# )
     ))

(defn navigator 
  ([] (navigator (.getRootFolder service)))
  ([root] (InventoryNavigator. root)))

(defn find-all
  "Find all entities of type"
  ([type] (find-all type (navigator)))
  ([type within] (.searchManagedEntities within type)))

(defn find*  
  "Find entity by type and name"
  ([type name] (find* type name (navigator)))
  ([type name within] (.searchManagedEntity within type name)))

(defn resource-pools [dcname]
  (find-all "ResourcePool" (navigator (find* "Datacenter" dcname))))

(def disk-format-types
  {:sparse VirtualMachineRelocateTransformation/sparse 
   :flat VirtualMachineRelocateTransformation/flat})

(defn relocation-spec [{:keys [datacenter]} {:keys [disk-format]}]
  (doto (VirtualMachineRelocateSpec.) 
    (.setTransform (disk-format-types disk-format))
    (.setPool (.getMOR (first (resource-pools datacenter))))))

(defn config-spec [{:keys [cpus memory]}]
  {:pre [(pos? cpus) (pos? memory)]}
  (doto (VirtualMachineConfigSpec.)
    (.setNumCPUs (int cpus)) 
    (.setMemoryMB memory)))

(defn clone-spec [allocation machine]
  (doto (VirtualMachineCloneSpec.)
    (.setLocation (relocation-spec allocation machine))
    (.setConfig (config-spec machine))
    (.setPowerOn false)
    (.setTemplate false)))

(defmacro wait-for [task]
  `(let [status# (.waitForTask ~task 1000 1000)]
     (when-not (= status# "success")
       (throw+ {:type ::task-fail :message (str "Vmware task failed with status:" status#)}))))

(defn clone [{:keys [datacenter] :as allocation} {:keys [template hostname] :as machine}]
  (with-service
    (if-let [vm (find* "VirtualMachine" template)]
      (wait-for (.cloneVM_Task vm (.getParent vm) hostname (clone-spec allocation machine)))
      (throw+ {:type ::missing-template :message (str "No matching template found " template)})
      )))

(def power-to-s
  {VirtualMachinePowerState/poweredOn :running 
   VirtualMachinePowerState/poweredOff :stopped 
   VirtualMachinePowerState/suspended :suspended})

(defn status [hostname]
  (with-service
    (let [vm (find* "VirtualMachine" hostname)]
      (-> (bean vm) :summary bean :runtime bean :powerState power-to-s ))))

(defn power-on [hostname]
  (with-service
    (let [vm (find* "VirtualMachine" hostname)]
      (wait-for (.powerOnVM_Task vm nil)))))

(defn power-off [hostname]
  (with-service
    (let [vm (find* "VirtualMachine" hostname)]
      (wait-for (.powerOffVM_Task vm)))))

(defn destroy [hostname]
  (with-service
    (let [vm (find* "VirtualMachine" hostname)]
      (wait-for (.destroy_Task vm)))))

(comment
  (try 
    (clone {:datacenter "playground"} 
           {:template "ubuntu-13.04_puppet-3.1" :hostname "bar" :disk-format :sparse :cpus 1 :memory 512})
    (catch Throwable e (error e)) 
    )
  (map deref (repeatedly 40 (fn []  (future (status "foo") ))))
  (status "foo")
  (power-on "foo")
  (power-off "foo")
  (destroy "foo") 
  (clojure.pprint/pprint  (bean (.getServerConnection (deref (first (services))))))
  (clojure.pprint/pprint (.getKey (:currentSession (bean (.getSessionManager (deref (first (services))))))))
  (clojure.pprint/pprint (.getKey (:currentSession (bean (.getSessionManager (deref (second (services))))))))
  )





<commit_msg>using find-vm and asserting power off on destroy<commit_after>(ns vsphere.vijava
  (:use 
    [slingshot.slingshot :only  [throw+ try+]]
    [clojure.core.strint :only  (<<)]
    [celestial.common :only (get! import-logging)])
  (:import 
    java.net.URL
    com.vmware.vim25.mo.InventoryNavigator
    com.vmware.vim25.VirtualMachineRelocateTransformation
    com.vmware.vim25.mo.ServiceInstance
    com.vmware.vim25.VirtualMachineCloneSpec
    com.vmware.vim25.VirtualMachineConfigSpec
    com.vmware.vim25.VirtualMachineRelocateSpec
    com.vmware.vim25.mo.Folder
    com.vmware.vim25.VirtualMachinePowerState
    com.vmware.vim25.mo.InventoryNavigator
    com.vmware.vim25.mo.ServiceInstance
    com.vmware.vim25.mo.Task
    com.vmware.vim25.mo.VirtualMachine 
    java.lang.Throwable 
    )
  )

(import-logging)

(def ^:dynamic service)

(defn connect [{:keys [url username password]}]
  (ServiceInstance. (URL. url) username password true))

(def services 
  (memoize 
    (fn []  
      (into [] 
            (repeatedly (get! :hypervisor :vsphere :session-count) #(agent (connect (get! :hypervisor :vsphere))))))))

(defn session-expired? [instance]
  (nil? (:currentSession (bean (.getSessionManager (deref (first (services))))))))

(defn renew-session [s]
  (if (session-expired? s)
    (do 
      (debug "Session expired renewing")
      (connect (get! :hypervisor :vsphere)))
    s))

(defmacro execute [body p]
  `(fn [s#]
     (binding [service (renew-session s#)]
       (try 
         (deliver ~p ~body) service) 
       (catch Throwable e# (deliver ~p e#) service))))

(defmacro with-service 
  "Uses recycled service instances see http://bit.ly/YRsiNo, 
  We try to keep all agents busy still rand isn't fair (cycle would work better)."
  [body]
  `(let [a# ((services) (rand-int (count (services)))) p# (promise)]
     (send a# (execute ~body p#)) 
     (let [res# @p#]
       (when (instance? Throwable res#)
         (throw res#))
       res#)
     ))

(defn navigator 
  ([] (navigator (.getRootFolder service)))
  ([root] (InventoryNavigator. root)))

(defn find-all
  "Find all entities of type"
  ([type] (find-all type (navigator)))
  ([type within] (.searchManagedEntities within type)))

(defn find*  
  "Find entity by type and name"
  ([type name*] (find* type name* (navigator)))
  ([type name* within] 
   (if-let [entity (.searchManagedEntity within type name*)]
     entity 
     (throw+ {:type ::missing-entity :message (str "No matching entity found " name*)}))))

(defn find-vm 
  "locates a vm or a template" 
  [name*]
  (find* "VirtualMachine" name*))

(defn resource-pools [dcname]
  (find-all "ResourcePool" (navigator (find* "Datacenter" dcname))))

(def disk-format-types
  {:sparse VirtualMachineRelocateTransformation/sparse 
   :flat VirtualMachineRelocateTransformation/flat})

(defn relocation-spec [{:keys [datacenter]} {:keys [disk-format]}]
  (doto (VirtualMachineRelocateSpec.) 
    (.setTransform (disk-format-types disk-format))
    (.setPool (.getMOR (first (resource-pools datacenter))))))

(defn config-spec [{:keys [cpus memory]}]
  {:pre [(pos? cpus) (pos? memory)]}
  (doto (VirtualMachineConfigSpec.)
    (.setNumCPUs (int cpus)) 
    (.setMemoryMB memory)))

(defn clone-spec [allocation machine]
  (doto (VirtualMachineCloneSpec.)
    (.setLocation (relocation-spec allocation machine))
    (.setConfig (config-spec machine))
    (.setPowerOn false)
    (.setTemplate false)))

(defmacro wait-for [task]
  `(let [status# (.waitForTask ~task 1000 1000)]
     (when-not (= status# "success")
       (throw+ {:type ::task-fail :message (str "Vmware task failed with status:" status#)}))))

(defn clone [{:keys [datacenter] :as allocation} {:keys [template hostname] :as machine}]
  (with-service
    (let [vm (find-vm template)]
      (wait-for (.cloneVM_Task vm (.getParent vm) hostname (clone-spec allocation machine))))))

(def power-to-s
  {VirtualMachinePowerState/poweredOn :running 
   VirtualMachinePowerState/poweredOff :stopped 
   VirtualMachinePowerState/suspended :suspended})

(defn status 
  "Get VM status"
  [hostname]
  (with-service
    (-> hostname find-vm bean :summary bean :runtime bean :powerState power-to-s )))

(defn power-on 
  "Power on VM"
  [hostname]
  (with-service
    (wait-for (.powerOnVM_Task (find-vm hostname) nil))))

(defn power-off 
  "Power off VM"
  [hostname]
  (with-service
    (wait-for (.powerOffVM_Task (find-vm hostname)))))

(defn destroy 
  "Destroy a vm, requires vm to be stopped"
  [hostname]
  {:pre [(= (status hostname) :stopped)]}
  (with-service (wait-for (.destroy_Task (find-vm hostname)))))

(comment
  (clone {:datacenter "playground"} {:template "ubuntu-13.04_puppet-3.1-with-tools" :hostname "bar" :disk-format :sparse :cpus 2 :memory 512}) 
  (with-service
    (:guestState (bean (.getGuest (find-vm "bar")))))
  (status "bar")
  (power-on "bar")
  (power-off "bar")
  (destroy "bar") 
  (clojure.pprint/pprint  (bean (.getServerConnection (deref (first (services))))))
  (clojure.pprint/pprint (.getKey (:currentSession (bean (.getSessionManager (deref (first (services))))))))
  (clojure.pprint/pprint (.getKey (:currentSession (bean (.getSessionManager (deref (second (services))))))))
  )






{'fraction_non_alphanumeric': 0.15322269394056348, 'fraction_numerical': 0.008297954457738324, 'mean_word_length': 3.8008337193144976, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3063428', 'n_tokens_mistral': 3724, 'n_tokens_neox': 3480, 'n_words': 904}