info.voidstar/tbnl.cnc

0.1.1-SNAPSHOT


C&C controls figureheads through mastermind

dependencies

org.clojure/clojure
1.6.0
org.clojure/core.async
0.1.303.0-886421-alpha
info.voidstar/tbnl.core
0.1.1-SNAPSHOT
alembic
0.2.1
dorothy
0.0.5



(this space intentionally left almost blank)
 

main entry into C&C

(ns cnc.main
  (:require (core main
                  init))
  (:require core.plugin.echo.main
            core.plugin.nrepl.main
            cnc.plugin.mastermind.main
            cnc.plugin.model.figurehead.visualize.main)
  (:gen-class))

the main entry

(defn -main
  [& args]
  (core.init/require-and-set-default-plugins core.plugin.echo
                                             core.plugin.nrepl
                                             cnc.plugin.mastermind
                                             cnc.plugin.model.figurehead.visualize)
  (apply core.main/main args))
 

connect to Mastermind

(ns cnc.plugin.mastermind.main
  (:require (core [init :as init]
                  [state :as state]
                  [bus :as bus]
                  [plugin :as plugin]))
  (:require [clojure.string :as str]
            [clojure.java.io :as io]
            [clojure.stacktrace :refer [print-stack-trace]]
            [clojure.pprint :refer [pprint]]
            [clojure.core.async
             :as async
             :refer [chan thread <!! >!!]])
  (:import
   (java.net Socket
             SocketTimeoutException)))
(def defaults
  (atom
   {
    :stop-unblock-tag :stop-cnc.plugin.mastermind
    :mastermind-port 1234
    :socket-timeout 15000
    :writer-buffer 1000
    }))
(defn populate-parse-opts-vector
  [current-parse-opts-vector]
  (init/add-to-parse-opts-vector [
                                  ["-a"
                                   "--mastermind-address ADDR"
                                   "mastermind address"]
                                  (let [option :mastermind-port
                                        default (option @defaults)]
                                    ["-p"
                                     (str "--"
                                          (name option)
                                          " [PORT]")
                                     (str "mastermind port")
                                     :default default
                                     :parse-fn (get-in @init/parse-opts-vector-helper
                                                       [:parse-fn :inet-port])])
                                  ]))
(defn init
  [options]
  (when (and (:mastermind-address options)
             (:mastermind-port options))
    true))
(defn run
  [options]
  (let [verbose (:verbose options)
        mastermind-address (:mastermind-address options)
        mastermind-port (:mastermind-port options)
        instance-id (state/get-state :instance-id)]
    (let [sock (Socket. ^String mastermind-address
                        ^int mastermind-port)]
      (plugin/blocking-jail [
                             ;; timeout
                             nil
                             ;; unblock-tag
                             (:stop-unblock-tag @defaults)
                             ;; finalization
                             (do
                               (.close sock))
                             ;; verbose
                             verbose
                             ]
                            (.setSoTimeout sock (:socket-timeout @defaults))
                            ;; reader thread
                            (thread
                              (with-open [^java.io.BufferedReader reader (io/reader sock)]
                                (plugin/looping-jail [
                                                      ;; stop condition
                                                      (plugin/get-state-entry :stop)
                                                      ;; finalization
                                                      (do
                                                        (.close sock))
                                                      ;; verbose
                                                      verbose
                                                      ]
                                                     (try
                                                       (when-let [line (.readLine reader)]
                                                         (try
                                                           (let [message (read-string line)
                                                                 topic (bus/get-message-topic message)
                                                                 content (bus/remove-message-topic message)]
                                                             (when verbose
                                                               (pprint [:mastermind :reader message]))
                                                             (case topic
                                                               :model-update
                                                               (do
                                                                 (bus/say!! :model-update content))
                                                               :information
                                                               (do
                                                                 (bus/say!! :information content))
                                                               :else))
                                                           (catch RuntimeException e
                                                             (when verbose
                                                               (print-stack-trace e)))))
                                                       (catch SocketTimeoutException e
                                                         (when verbose
                                                           (print-stack-trace e)))))))
                            ;; writer thread
                            (thread
                              (with-open [^java.io.BufferedWriter writer (io/writer sock)]
                                (let [ch (chan (:writer-buffer @defaults))]
                                  (bus/register-listener ch)
                                  (plugin/looping-jail [
                                                        ;; stop condition
                                                        (plugin/get-state-entry :stop)
                                                        ;; finalization
                                                        (do
                                                          (bus/unregister-listener ch)
                                                          (.close sock))
                                                        ;; verbose
                                                        verbose
                                                        ]
                                                       (let [message (<!! ch)
                                                             topic (bus/get-message-topic message)
                                                             content (bus/remove-message-topic message)]
                                                         (cond
                                                          ;; do NOT echo these topics back
                                                          (not (contains? #{:information :model-update} topic))
                                                          (let [message (bus/build-message topic
                                                                                           (cond
                                                                                            (map? content)
                                                                                            (merge content
                                                                                                   {:instance instance-id})
                                                                                            :else
                                                                                            {:instance instance-id
                                                                                             :content message}))]
                                                            (when verbose
                                                              (pprint [:mastermind :writer message]))
                                                            (.write writer
                                                                    (prn-str message))
                                                            (.flush writer))))))))))))
(defn stop
  []
  (plugin/set-state-entry :cnc.plugin.mastermind
                          :stop true)
  (plugin/unblock-thread (:stop-unblock-tag @defaults)))

the config map

(def config-map
  {:populate-parse-opts-vector populate-parse-opts-vector
   :init init
   :run run
   :stop stop
   :param {:priority 90
           :auto-restart true}})
 

helpers for visualizing figurehead monitor model

(ns cnc.plugin.model.figurehead.visualize.helper
  (:use
   [clojure.core.async :as async
    :only [chan 
           close!
           go thread 
           >! <! >!! <!!
           alts! alts!!
           timeout]]
   dorothy.core)
  (:require
   [clojure.string :as str]
   [clojure.java.io :as io]))
(defn visualize
  [model viz-root viz-counter]
  (let [g (atom [])
        packages (-> model :packages keys)
        cur (atom 0)]
    (doseq [package packages]
      (let [pkg (get-in model [:packages package])]
        (swap! cur inc)
        ;; subgraphs by packages
        (swap! g conj
               (subgraph package
                         (apply vector 
                                {:label (:name pkg)}
                                (node-attrs {:style :filled
                                             :color (str/join " "
                                                              [(double (* @cur (/ 1 (count packages))))
                                                               1
                                                               1])})
                                (->> (:activities pkg)
                                     (map (fn [[id {:keys [name flags]}]]
                                            (vector
                                             id
                                             {
                                              :label name
                                              :shape 
                                              (cond
                                               (contains? flags
                                                          :category-home)
                                               :house
                                               (contains? flags
                                                          :category-launcher)
                                               :box
                                               :other
                                               :ellipse)
                                              })))))))))
    ;; edges
    (let [edges (get model :edges)]
      (doseq [from (keys edges)
              to (-> edges (get-in [from :to]) keys)]
        (apply swap! g conj (repeat
                             (get-in edges [from :to to])
                             [from to]))))
    ;; draw the whole graph
    (let [d (dot (digraph @g))
          fname-root (str viz-root "_" viz-counter)]
      (spit (str fname-root ".dot") d)
      (save! d (str fname-root ".pdf") {:format :pdf}) 
      (save! d (str fname-root ".png") {:format :png}))))
 

visualize figurehead monitor model

(ns cnc.plugin.model.figurehead.visualize.main
  (:require (core [init :as init]
                  [state :as state]
                  [bus :as bus]
                  [plugin :as plugin]))
  (:require (cnc.plugin.model.figurehead.visualize [helper :as helper]))
  (:require [clojure.core.async :as async :refer [chan close!
                                                  go <! >! alts!]]
            [clojure.java.io :as io]))
(def defaults
  (atom
   {
    :stop-unblock-tag :stop-cnc.plugin.model.figurehead.visualization
    }))
(defn populate-parse-opts-vector
  [current-parse-opts-vector]
  (init/add-to-parse-opts-vector [
                                  [nil
                                   "--viz"
                                   "visualize figurehead model"]
                                  [nil
                                   "--viz-root [ROOT]"
                                   "visualization root file name"
                                   :default "viz"
                                   ]
                                  ]))
(defn init
  [options]
  (let [visualize (:visualize options)
        viz-root (:viz-root options)]
    (when (and viz-root)
      true)))
(defn run
  [options]
  (let [verbose (:verbose options)
        viz-root (:viz-root options)
        ch (chan)
        viz-counter (atom 0)]
    (plugin/blocking-jail [
                           ;; timeout
                           nil
                           ;; unblock-tag
                           (:stop-unblock-tag @defaults)
                           ;; finalization
                           (do
                             (bus/unsub-topic ch :model-update))
                           ;; verbose
                           verbose
                           ]
                          (bus/sub-topic ch :model-update)
                          ;; get the initial model
                          (bus/say!! :command {:topic :get-model :what {}})
                          ;; listen for model update
                          (go
                            (loop [said (<! ch)]
                              (let [topic (bus/get-message-topic said)
                                    content (bus/remove-message-topic said)]
                                (case topic
                                  :model-update
                                  (do
                                    (let [type (:type content)
                                          model (:model content)]
                                      (case type
                                        :figurehead
                                        (do
                                          (swap! viz-counter inc)
                                          (helper/visualize model viz-root @viz-counter))
                                        :else)))
                                  :else))
                              (recur (<! ch)))))))
(defn stop
  []
  (plugin/set-state-entry :cnc.plugin.model.figurehead.visualization
                          :stop true)
  (plugin/unblock-thread (:stop-unblock-tag @defaults)))

the config map

(def config-map
  {
   :populate-parse-opts-vector populate-parse-opts-vector
   :init init
   :run run
   :stop stop
   :param {
           :priority 1
           :auto-restart true
           }})