Index: Makefile
==================================================================
--- Makefile
+++ Makefile
@@ -28,11 +28,12 @@
cookie.scm mutils.scm mtargs.scm apimod.scm \
configfmod.scm commonmod.scm dbmod.scm rmtmod.scm \
debugprint.scm mtver.scm csv-xml.scm servermod.scm \
hostinfo.scm adjutant.scm processmod.scm testsmod.scm \
itemsmod.scm keysmod.scm mtmod.scm rmtmod.scm \
- tasksmod.scm pgdb.scm launchmod.o runsmod.scm
+ tasksmod.scm pgdb.scm launchmod.o runsmod.scm \
+ http-transportmod.scm portloggermod.scm
GUISRCF = dashboard-context-menu.scm dashboard-tests.scm \
dashboard-guimonitor.scm gutils.scm dcommon.scm tree.scm \
vg.scm
@@ -74,10 +75,14 @@
mofiles/apimod.o : mofiles/tasksmod.o
mofiles/tasksmod.o : mofiles/pgdb.o mofiles/dbmod.o
mofiles/rmtmod.o : mofiles/itemsmod.o
mofiles/launchmod.o : mofiles/runsmod.o
+mofiles/servermod.o : mofiles/http-transportmod.o
+mofiles/http-transportmod.o : mofiles/dbmod.o mofiles/portloggermod.o
+mofiles/testsmod.o : mofiles/itemsmod.o
+mofiles/portlogger.o : mofiles/tasksmod.o
dashboard.o megatest.o : db_records.scm megatest-fossil-hash.scm
ADTLSCR=mt_laststep mt_runstep mt_ezstep
HELPERS=$(addprefix $(PREFIX)/bin/,$(ADTLSCR))
@@ -401,5 +406,11 @@
targets:
@grep : Makefile | perl -ne '/^([A-Za-z0-9_-]+):/ && print "$$1\n"'
unit :
cd tests;make unit
+
+DEPSFILES=*mod.scm adjutant.scm
+
+deps.pdf : $(DEPSFILES)
+ gendeps deps $(DEPSFILES)
+ dot deps.dot -Tpdf -o deps.pdf
Index: common.scm
==================================================================
--- common.scm
+++ common.scm
@@ -449,10 +449,59 @@
)
)
0)
+(define (make-and-init-remote)
+ (make-remote hh-dat: (common:get-homehost)
+ server-info: (if *toppath* (server:check-if-running *toppath*) #f)
+ server-timeout: (server:expiration-timeout)))
+
+;; TODO: for multiple areas, we will have multiple watchdogs; and multiple threads to manage
+(set! *watchdog* (make-thread
+ (lambda ()
+ (handle-exceptions
+ exn
+ (begin
+ (print-call-chain)
+ (print " message: " ((condition-property-accessor 'exn 'message) exn) ", exn=" exn))
+ (common:watchdog)))
+ "Watchdog thread"))
+
+ ;;(if (not (args:get-arg "-server"))
+ ;; (thread-start! *watchdog*)) ;; if starting a server; wait till we get to running state before kicking off watchdog
+ (let* ((no-watchdog-args
+ '("-list-runs"
+ "-testdata-csv"
+ "-list-servers"
+ "-server"
+ "-adjutant"
+ "-list-disks"
+ "-list-targets"
+ "-show-runconfig"
+ ;;"-list-db-targets"
+ "-show-runconfig"
+ "-show-config"
+ "-show-cmdinfo"
+ "-cleanup-db"
+ ))
+ (no-watchdog-argvals (list '("-archive" . "replicate-db")))
+ (start-watchdog-specail-arg-val (let loop ((hed (car no-watchdog-argvals))
+ (tail (cdr no-watchdog-argvals)))
+ ;; (print "hed" hed " arg " (args:get-arg (car hed)) " val:" (cdr hed) " eql" (equal? (args:get-arg (car hed)) (cdr hed)))
+ (if (equal? (args:get-arg (car hed)) (cdr hed))
+ #f
+ (if (null? tail)
+ #t
+ (loop (car tail) (cdr tail))))))
+ (no-watchdog-args-vals (filter (lambda (x) x)
+ (map args:get-arg no-watchdog-args)))
+ (start-watchdog (and (null? no-watchdog-args-vals) start-watchdog-specail-arg-val)))
+ ;(print "no-watchdog-args="no-watchdog-args "no-watchdog-args-vals="no-watchdog-args-vals " start-watchdog-specail-arg-val:" start-watchdog-specail-arg-val " start-watchdog:" start-watchdog)
+ (if start-watchdog
+ (thread-start! *watchdog*)))
+
;;======================================================================
;; TODO: for multiple areas, we will have multiple watchdogs; and multiple threads to manage
(define (common:watchdog)
(debug:print-info 13 *default-log-port* "common:watchdog entered.")
(if (launch:setup)
@@ -477,14 +526,11 @@
;;(debug:print 1 *default-log-port* "INFO: ["(common:human-time)"] Syncer started (method="syncer")")
)))
(debug:print-info 13 *default-log-port* "watchdog done."))
(debug:print-info 13 *default-log-port* "no need for watchdog on non-homehost"))))
-(define (make-and-init-remote)
- (make-remote hh-dat: (common:get-homehost)
- server-info: (if *toppath* (server:check-if-running *toppath*) #f)
- server-timeout: (server:expiration-timeout)))
+
;;======================================================================
;; currently the primary job of the watchdog is to run the sync back to megatest.db from the db in /tmp
;; if we are on the homehost and we are a server (by definition we are on the homehost if we are a server)
;;
(define (common:readonly-watchdog dbstruct)
Index: commonmod.scm
==================================================================
--- commonmod.scm
+++ commonmod.scm
@@ -104,10 +104,12 @@
(define user (getenv "USER"))
;; Globals
;;
(define *server-loop-heart-beat* (current-seconds))
+
+(define *watchdog* #f)
;; copied from egg call-with-environment-variables
;;
(define (call-with-environment-variables variables thunk)
;; @("Sets up environment variable via dynamic-wind which are taken down after thunk."
@@ -3551,7 +3553,25 @@
(vector->list (hostinfo-addresses (hostname->hostinfo hostname))))
(string-intersperse
(map number->string
(u8vector->list
(if res res (hostname->ip hostname)))) ".")))
+
+(define (tests:readlines filename)
+ (call-with-input-file filename
+ (lambda (p)
+ (let loop ((line (read-line p))
+ (result '()))
+ (if (eof-object? line)
+ (reverse result)
+ (loop (read-line p) (cons line result)))))))
+
+;; timeout is hms string: 1h 5m 3s, default is 1 minute
+;;
+(define (server:expiration-timeout)
+ (let ((tmo (configf:lookup *configdat* "server" "timeout")))
+ (if (and (string? tmo)
+ (common:hms-string->seconds tmo)) ;; BUG: hms-string->seconds is broken, if given "10" returns 0. Also, it doesn't belong in this logic unless the string->number is changed below
+ (* 3600 (string->number tmo))
+ 60)))
)
Index: http-transport.scm
==================================================================
--- http-transport.scm
+++ http-transport.scm
@@ -14,717 +14,5 @@
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with Megatest. If not, see .
-;; (require-extension (srfi 18) extras tcp s11n)
-;;
-;;
-;; (use srfi-1 posix regex regex-case srfi-69 hostinfo md5 message-digest posix-extras)
-;;
-;; (use spiffy uri-common intarweb http-client spiffy-request-vars intarweb spiffy-directory-listing)
-;;
-;; Configurations for server
-(tcp-buffer-size 2048)
-(max-connections 2048)
-
-;; (declare (unit http-transport))
-;;
-;; (declare (uses common))
-;; (declare (uses db))
-;; (declare (uses tests))
-;; (declare (uses tasks)) ;; tasks are where stuff is maintained about what is running.
-;; (declare (uses server))
-;; ;; (declare (uses daemon))
-;; (declare (uses portlogger))
-;; (declare (uses rmt))
-;;
-;; (include "common_records.scm")
-;; (include "db_records.scm")
-;; (include "js-path.scm")
-
-;; (require-library stml)
-(define (http-transport:make-server-url hostport)
- (if (not hostport)
- #f
- (conc "http://" (car hostport) ":" (cadr hostport))))
-
-;;======================================================================
-;; S E R V E R
-;; ======================================================================
-
-;; Call this to start the actual server
-;;
-
-(define *db:process-queue-mutex* (make-mutex))
-
-(define (http-transport:run hostn)
- ;; Configurations for server
- (tcp-buffer-size 2048)
- (max-connections 2048)
- (debug:print 2 *default-log-port* "Attempting to start the server ...")
- (let* ((db #f) ;; (open-db)) ;; we don't want the server to be opening and closing the db unnecesarily
- (hostname (get-host-name))
- (ipaddrstr (let ((ipstr (if (string=? "-" hostn)
- ;; (string-intersperse (map number->string (u8vector->list (hostname->ip hostname))) ".")
- (server:get-best-guess-address hostname)
- #f)))
- (if ipstr ipstr hostn))) ;; hostname)))
- (start-port (portlogger:open-run-close portlogger:find-port))
- (link-tree-path (common:get-linktree))
- (tmp-area (common:get-db-tmp-area))
- (start-file (conc tmp-area "/.server-start")))
- (debug:print-info 0 *default-log-port* "portlogger recommended port: " start-port)
- ;; set some parameters for the server
- (root-path (if link-tree-path
- link-tree-path
- (current-directory))) ;; WARNING: SECURITY HOLE. FIX ASAP!
- (handle-directory spiffy-directory-listing)
- (handle-exception (lambda (exn chain)
- (signal (make-composite-condition
- (make-property-condition
- 'server
- 'message "server error")))))
-
- ;; http-transport:handle-directory) ;; simple-directory-handler)
- ;; Setup the web server and a /ctrl interface
- ;;
- (vhost-map `(((* any) . ,(lambda (continue)
- ;; open the db on the first call
- ;; This is were we set up the database connections
- (let* (($ (request-vars source: 'both))
- (dat ($ 'dat))
- (res #f))
- (cond
- ((equal? (uri-path (request-uri (current-request)))
- '(/ "api"))
- (send-response body: (api:process-request *dbstruct-db* $) ;; the $ is the request vars proc
- headers: '((content-type text/plain)))
- (mutex-lock! *heartbeat-mutex*)
- (set! *db-last-access* (current-seconds))
- (mutex-unlock! *heartbeat-mutex*))
- ((equal? (uri-path (request-uri (current-request)))
- '(/ ""))
- (send-response body: (http-transport:main-page)))
- ((equal? (uri-path (request-uri (current-request)))
- '(/ "json_api"))
- (send-response body: (http-transport:main-page)))
- ((equal? (uri-path (request-uri (current-request)))
- '(/ "runs"))
- (send-response body: (http-transport:main-page)))
- ((equal? (uri-path (request-uri (current-request)))
- '(/ any))
- (send-response body: "hey there!\n"
- headers: '((content-type text/plain))))
- ((equal? (uri-path (request-uri (current-request)))
- '(/ "hey"))
- (send-response body: "hey there!\n"
- headers: '((content-type text/plain))))
- ((equal? (uri-path (request-uri (current-request)))
- '(/ "jquery3.1.0.js"))
- (send-response body: (http-transport:show-jquery)
- headers: '((content-type application/javascript))))
- ((equal? (uri-path (request-uri (current-request)))
- '(/ "test_log"))
- (send-response body: (http-transport:html-test-log $)
- headers: '((content-type text/HTML))))
- ((equal? (uri-path (request-uri (current-request)))
- '(/ "dashboard"))
- (send-response body: (http-transport:html-dboard $)
- headers: '((content-type text/HTML))))
- (else (continue))))))))
- (handle-exceptions
- exn
- (debug:print 0 *default-log-port* "Failed to create file " start-file ", exn=" exn)
- (with-output-to-file start-file (lambda ()(print (current-process-id)))))
- (http-transport:try-start-server ipaddrstr start-port)))
-
-;; This is recursively run by http-transport:run until sucessful
-;;
-(define (http-transport:try-start-server ipaddrstr portnum)
- (let ((config-hostname (configf:lookup *configdat* "server" "hostname"))
- (config-use-proxy (equal? (configf:lookup *configdat* "client" "use-http_proxy") "yes")))
- (if (not config-use-proxy)
- (determine-proxy (constantly #f)))
- (debug:print-info 0 *default-log-port* "http-transport:try-start-server time=" (seconds->time-string (current-seconds)) " ipaddrsstr=" ipaddrstr " portnum=" portnum " config-hostname=" config-hostname)
- (handle-exceptions
- exn
- (begin
- (print-error-message exn)
- (if (< portnum 64000)
- (begin
- (debug:print 0 *default-log-port* "WARNING: attempt to start server failed. Trying again ...")
- (debug:print 0 *default-log-port* " message: " ((condition-property-accessor 'exn 'message) exn))
- (debug:print 5 *default-log-port* "exn=" (condition->list exn))
- (portlogger:open-run-close portlogger:set-failed portnum)
- (debug:print 0 *default-log-port* "WARNING: failed to start on portnum: " portnum ", trying next port")
- (thread-sleep! 0.1)
-
- ;; get_next_port goes here
- (http-transport:try-start-server ipaddrstr
- (portlogger:open-run-close portlogger:find-port)))
- (begin
- (print "ERROR: Tried and tried but could not start the server"))))
- ;; any error in following steps will result in a retry
- (set! *server-info* (list ipaddrstr portnum))
- (debug:print 0 *default-log-port* "INFO: Trying to start server on " ipaddrstr ":" portnum)
- ;; This starts the spiffy server
- ;; NEED WAY TO SET IP TO #f TO BIND ALL
- ;; (start-server bind-address: ipaddrstr port: portnum)
- (if config-hostname ;; this is a hint to bind directly
- (start-server port: portnum bind-address: (if (equal? config-hostname "-")
- ipaddrstr
- config-hostname))
- (start-server port: portnum))
- (portlogger:open-run-close portlogger:set-port portnum "released")
- (debug:print 1 *default-log-port* "INFO: server has been stopped"))))
-
-;;======================================================================
-;; S E R V E R U T I L I T I E S
-;;======================================================================
-
-;;======================================================================
-;; C L I E N T S
-;;======================================================================
-
-(define *http-mutex* (make-mutex))
-
-;; NOTE: Large block of code from 32436b426188080f72fceb6894af541fbad9921e removed here
-;; I'm pretty sure it is defunct.
-
-;; This next block all imported en-mass from the api branch
-(define *http-requests-in-progress* 0)
-(define *http-connections-next-cleanup* (current-seconds))
-
-(define (http-transport:get-time-to-cleanup)
- (let ((res #f))
- (mutex-lock! *http-mutex*)
- (set! res (> (current-seconds) *http-connections-next-cleanup*))
- (mutex-unlock! *http-mutex*)
- res))
-
-(define (http-transport:inc-requests-count)
- (mutex-lock! *http-mutex*)
- (set! *http-requests-in-progress* (+ 1 *http-requests-in-progress*))
- ;; Use this opportunity to slow things down iff there are too many requests in flight
- (if (> *http-requests-in-progress* 5)
- (begin
- (debug:print-info 0 *default-log-port* "Whoa there buddy, ease up...")
- (thread-sleep! 1)))
- (mutex-unlock! *http-mutex*))
-
-(define (http-transport:dec-requests-count proc)
- (mutex-lock! *http-mutex*)
- (proc)
- (set! *http-requests-in-progress* (- *http-requests-in-progress* 1))
- (mutex-unlock! *http-mutex*))
-
-(define (http-transport:dec-requests-count-and-close-all-connections)
- (set! *http-requests-in-progress* (- *http-requests-in-progress* 1))
- (let loop ((etime (+ (current-seconds) 5))) ;; give up in five seconds
- (if (> *http-requests-in-progress* 0)
- (if (> etime (current-seconds))
- (begin
- (thread-sleep! 0.052)
- (loop etime))
- (debug:print-error 0 *default-log-port* "requests still in progress after 5 seconds of waiting. I'm going to pass on cleaning up http connections"))
- (close-idle-connections!)))
- (set! *http-connections-next-cleanup* (+ (current-seconds) 10))
- (mutex-unlock! *http-mutex*))
-
-(define (http-transport:inc-requests-and-prep-to-close-all-connections)
- (mutex-lock! *http-mutex*)
- (set! *http-requests-in-progress* (+ 1 *http-requests-in-progress*)))
-
-;; Send "cmd" with json payload "params" to serverdat and receive result
-;;
-(define (http-transport:client-api-send-receive run-id serverdat cmd params #!key (numretries 3)(area-dat #f))
- (let* ((fullurl (if (vector? serverdat)
- (http-transport:server-dat-get-api-req serverdat)
- (begin
- (debug:print 0 *default-log-port* "FATAL ERROR: http-transport:client-api-send-receive called with no server info")
- (exit 1))))
- (res (vector #f "uninitialized"))
- (success #t)
- (sparams (db:obj->string params transport: 'http))
- (runremote (or area-dat *runremote*))
- (server-id (if (vector? serverdat)
- (http-transport:server-dat-get-server-id serverdat)
- (begin
- (debug:print 0 *default-log-port* "FATAL ERROR: http-transport:client-api-send-receive called with no server info")
- (exit 1)))))
- (debug:print-info 11 *default-log-port* "cmd=" cmd " fullurl=" fullurl " server-id=" server-id " current time:" (current-seconds))
-
- ;; set up the http-client here
- (max-retry-attempts 1)
- ;; consider all requests indempotent
- (retry-request? (lambda (request)
- #f))
- ;; send the data and get the response
- ;; extract the needed info from the http data and
- ;; process and return it.
- (let* ((send-recieve (lambda ()
- (mutex-lock! *http-mutex*)
- ;; (condition-case (with-input-from-request "http://localhost"; #f read-lines)
- ;; ((exn http client-error) e (print e)))
- (set! res (vector ;;; DON'T FORGET - THIS IS THE CLIENT SIDE! NOTE: consider moving this to client.scm since we are only supporting http transport at this time.
- success
- (db:string->obj
- (handle-exceptions
- exn
- (let ((call-chain (get-call-chain))
- (msg ((condition-property-accessor 'exn 'message) exn)))
- (set! success #f)
- (if (debug:debug-mode 1)
- (debug:print-info 0 *default-log-port* "couldn't talk to server, trying again ...")
- (begin
- (debug:print 0 *default-log-port* "WARNING: failure in with-input-from-request to " fullurl ".")
- (debug:print 0 *default-log-port* " message: " msg ", exn=" exn)
- (debug:print 0 *default-log-port* " cmd: " cmd " params: " params " key:" (or server-id "thekey"))
- (debug:print 0 *default-log-port* " call-chain: " call-chain)))
- (if runremote
- (remote-conndat-set! runremote #f))
- ;; Killing associated server to allow clean retry.")
- ;; (tasks:kill-server-run-id run-id) ;; better to kill the server in the logic that called this routine?
- (mutex-unlock! *http-mutex*)
- ;;; (signal (make-composite-condition
- ;;; (make-property-condition 'commfail 'message "failed to connect to server")))
- ;;; "communications failed"
- (db:obj->string #f))
- (with-input-from-request ;; was dat
- fullurl
- (list (cons 'key (or server-id "thekey"))
- (cons 'cmd cmd)
- (cons 'params sparams))
- read-string))
- transport: 'http)
- 0)) ;; added this speculatively
- ;; Shouldn't this be a call to the managed call-all-connections stuff above?
- (close-idle-connections!)
- (mutex-unlock! *http-mutex*)
- ))
- (time-out (lambda ()
- (thread-sleep! 45)
- (debug:print 0 *default-log-port* "WARNING: send-receive took more than 45 seconds!!")
- #f))
- (th1 (make-thread send-recieve "with-input-from-request"))
- (th2 (make-thread time-out "time out")))
- (thread-start! th1)
- (thread-start! th2)
- (thread-join! th1)
- (vector-set! res 0 success)
- (thread-terminate! th2)
- (if (vector? res)
- (if (vector-ref res 0) ;; this is the first flag or the second flag?
- (let* ((res-dat (vector-ref res 1)))
- (if (and (string? res-dat) (string-contains res-dat "server-id mismatch"))
- (signal (make-composite-condition
- (make-property-condition
- 'servermismatch
- 'message (vector-ref res 1))))
- res)) ;; this is the *inner* vector? seriously? why?
- (if (debug:debug-mode 11)
- (let ((call-chain (get-call-chain))) ;; note: this code also called in nmsg-transport - consider consolidating it
- (print-call-chain (current-error-port))
- (debug:print-error 11 *default-log-port* "error above occured at server, res=" res) ;; " message: " ((condition-property-accessor 'exn 'message) exn))
- (debug:print 11 *default-log-port* " server call chain:")
- (pp (vector-ref res 1) (current-error-port))
- (signal (vector-ref res 0)))
- res))
- (signal (make-composite-condition
- (make-property-condition
- 'timeout
- 'message "nmsg-transport:client-api-send-receive-raw timed out talking to server")))))))
-
-;; careful closing of connections stored in *runremote*
-;;
-(define (http-transport:close-connections #!key (area-dat #f))
- (let* ((runremote (or area-dat *runremote*))
- (server-dat (if runremote
- (remote-conndat runremote)
- #f))) ;; (hash-table-ref/default *runremote* run-id #f)))
- (if (vector? server-dat)
- (let ((api-dat (http-transport:server-dat-get-api-uri server-dat)))
- (handle-exceptions
- exn
- (begin
- (print-call-chain *default-log-port*)
- (debug:print-error 0 *default-log-port* " closing connection failed with error: " ((condition-property-accessor 'exn 'message) exn) ", exn=" exn))
- (close-connection! api-dat)
- ;;(close-idle-connections!)
- #t))
- #f)))
-
-
-(define (make-http-transport:server-dat)(make-vector 6))
-(define (http-transport:server-dat-get-iface vec) (vector-ref vec 0))
-(define (http-transport:server-dat-get-port vec) (vector-ref vec 1))
-(define (http-transport:server-dat-get-api-uri vec) (vector-ref vec 2))
-(define (http-transport:server-dat-get-api-url vec) (vector-ref vec 3))
-(define (http-transport:server-dat-get-api-req vec) (vector-ref vec 4))
-(define (http-transport:server-dat-get-last-access vec) (vector-ref vec 5))
-;(define (http-transport:server-dat-get-socket vec) (vector-ref vec 6))
-(define (http-transport:server-dat-get-server-id vec) (vector-ref vec 6))
-
-(define (http-transport:server-dat-make-url vec)
- (if (and (http-transport:server-dat-get-iface vec)
- (http-transport:server-dat-get-port vec))
- (conc "http://"
- (http-transport:server-dat-get-iface vec)
- ":"
- (http-transport:server-dat-get-port vec))
- #f))
-
-(define (http-transport:server-dat-update-last-access vec)
- (if (vector? vec)
- (vector-set! vec 5 (current-seconds))
- (begin
- (print-call-chain (current-error-port))
- (debug:print-error 0 *default-log-port* "call to http-transport:server-dat-update-last-access with non-vector!!"))))
-
-;;
-;; connect
-;;
-(define (http-transport:client-connect iface port server-id)
- (let* ((api-url (conc "http://" iface ":" port "/api"))
- (api-uri (uri-reference (conc "http://" iface ":" port "/api")))
- (api-req (make-request method: 'POST uri: api-uri))
- (server-dat (vector iface port api-uri api-url api-req (current-seconds) server-id)))
- server-dat))
-
-;; run http-transport:keep-running in a parallel thread to monitor that the db is being
-;; used and to shutdown after sometime if it is not.
-;;
-(define (http-transport:keep-running)
- ;; if none running or if > 20 seconds since
- ;; server last used then start shutdown
- ;; This thread waits for the server to come alive
- (debug:print-info 0 *default-log-port* "Starting the sync-back, keep alive thread in server")
- (let* ((sdat #f)
- (tmp-area (common:get-db-tmp-area))
- (started-file (conc tmp-area "/.server-started"))
- (server-start-time (current-seconds))
- (server-info (let loop ((start-time (current-seconds))
- (changed #t)
- (last-sdat "not this"))
- (begin ;; let ((sdat #f))
- (thread-sleep! 0.01)
- (debug:print-info 0 *default-log-port* "Waiting for server alive signature")
- (mutex-lock! *heartbeat-mutex*)
- (set! sdat *server-info*)
- (mutex-unlock! *heartbeat-mutex*)
- (if (and sdat
- (not changed)
- (> (- (current-seconds) start-time) 2))
- (begin
- (debug:print-info 0 *default-log-port* "Received server alive signature")
- (common:save-pkt `((action . alive)
- (T . server)
- (pid . ,(current-process-id))
- (ipaddr . ,(car sdat))
- (port . ,(cadr sdat)))
- *configdat* #t)
- sdat)
- (begin
- (debug:print-info 0 *default-log-port* "Still waiting, last-sdat=" last-sdat)
- (sleep 4)
- (if (> (- (current-seconds) start-time) 120) ;; been waiting for two minutes
- (begin
- (debug:print-error 0 *default-log-port* "transport appears to have died, exiting server")
- (common:save-pkt `((action . died)
- (T . server)
- (pid . ,(current-process-id))
- (ipaddr . ,(car sdat))
- (port . ,(cadr sdat))
- (msg . "Transport died?"))
- *configdat* #t)
- (exit))
- (loop start-time
- (equal? sdat last-sdat)
- sdat)))))))
- (iface (car server-info))
- (port (cadr server-info))
- (last-access 0)
- (server-timeout (server:expiration-timeout))
- (server-going #f)
- (server-log-file (args:get-arg "-log"))) ;; always set when we are a server
-
- (handle-exceptions
- exn
- (debug:print 0 *default-log-port* "Failed to create " started-file ", exn=" exn)
- (with-output-to-file started-file (lambda ()(print (current-process-id)))))
-
- (let loop ((count 0)
- (server-state 'available)
- (bad-sync-count 0)
- (start-time (current-milliseconds)))
- ;; Use this opportunity to sync the tmp db to megatest.db
- (if (not server-going) ;; *dbstruct-db*
- (begin
- (debug:print 0 *default-log-port* "SERVER: dbprep")
- (set! *dbstruct-db* (db:setup #t)) ;; run-id))
- (set! server-going #t)
- (debug:print 0 *default-log-port* "SERVER: running, megatest version: " (common:get-full-version)) ;; NOTE: the server is NOT yet marked as running in the log. We do that in the keep-running routine.
- (thread-start! *watchdog*)))
-
- ;; when things go wrong we don't want to be doing the various queries too often
- ;; so we strive to run this stuff only every four seconds or so.
- (let* ((sync-time (- (current-milliseconds) start-time))
- (rem-time (quotient (- 4000 sync-time) 1000)))
- (if (and (<= rem-time 4)
- (> rem-time 0))
- (thread-sleep! rem-time)))
-
- (if (< count 1) ;; 3x3 = 9 secs aprox
- (loop (+ count 1) 'running bad-sync-count (current-milliseconds)))
-
- ;; Check that iface and port have not changed (can happen if server port collides)
- (mutex-lock! *heartbeat-mutex*)
- (set! sdat *server-info*)
- (mutex-unlock! *heartbeat-mutex*)
-
- (if (not (equal? sdat (list iface port)))
- (let ((new-iface (car sdat))
- (new-port (cadr sdat)))
- (debug:print-info 0 *default-log-port* "WARNING: interface changed, refreshing iface and port info")
- (set! iface new-iface)
- (set! port new-port)
- (if (not *server-id*)
- (set! *server-id* (server:mk-signature)))
- (debug:print 0 *default-log-port* (current-seconds) (current-directory) (current-process-id) (argv))
- (debug:print 0 *default-log-port* "SERVER STARTED: " iface ":" port " AT " (current-seconds) " server-id: " *server-id*)
- (flush-output *default-log-port*)))
-
- ;; Transfer *db-last-access* to last-access to use in checking that we are still alive
- (mutex-lock! *heartbeat-mutex*)
- (set! last-access *db-last-access*)
- (mutex-unlock! *heartbeat-mutex*)
-
- (if (common:low-noise-print 120 (conc "server running on " iface ":" port))
- (begin
- (if (not *server-id*)
- (set! *server-id* (server:mk-signature)))
- (debug:print 0 *default-log-port* (current-seconds) (current-directory) (current-process-id) (argv))
- (debug:print 0 *default-log-port* "SERVER STARTED: " iface ":" port " AT " (current-seconds) " server-id: " *server-id*)
- (flush-output *default-log-port*)))
- (if (common:low-noise-print 60 "dbstats")
- (begin
- (debug:print 0 *default-log-port* "Server stats:")
- (db:print-current-query-stats)))
- (let* ((hrs-since-start (/ (- (current-seconds) server-start-time) 3600)))
- (cond
- ((and *server-run*
- (> (+ last-access server-timeout)
- (current-seconds)))
- (if (common:low-noise-print 120 "server continuing")
- (debug:print-info 0 *default-log-port* "Server continuing, seconds since last db access: " (- (current-seconds) last-access))
- (let ((curr-time (current-seconds)))
- (handle-exceptions
- exn
- (debug:print 0 *default-log-port* "ERROR: Failed to change timestamp on log file " server-log-file ". Are you out of space on that disk? exn=" exn)
- (if (not *server-overloaded*)
- (set-file-times! server-log-file curr-time curr-time)))))
- (loop 0 server-state bad-sync-count (current-milliseconds)))
- (else
- (debug:print-info 0 *default-log-port* "Server timed out. seconds since last db access: " (- (current-seconds) last-access))
- (http-transport:server-shutdown port)))))))
-
-(define (http-transport:server-shutdown port)
- (begin
- ;;(BB> "http-transport:server-shutdown called")
- (debug:print-info 0 *default-log-port* "Starting to shutdown the server. pid="(current-process-id))
- ;;
- ;; start_shutdown
- ;;
- (set! *time-to-exit* #t) ;; tell on-exit to be fast as we've already cleaned up
- (portlogger:open-run-close portlogger:set-port port "released")
- (thread-sleep! 1)
-
- ;; (debug:print-info 0 *default-log-port* "Max cached queries was " *max-cache-size*)
- ;; (debug:print-info 0 *default-log-port* "Number of cached writes " *number-of-writes*)
- ;; (debug:print-info 0 *default-log-port* "Average cached write time "
- ;; (if (eq? *number-of-writes* 0)
- ;; "n/a (no writes)"
- ;; (/ *writes-total-delay*
- ;; *number-of-writes*))
- ;; " ms")
- ;; (debug:print-info 0 *default-log-port* "Number non-cached queries " *number-non-write-queries*)
- ;; (debug:print-info 0 *default-log-port* "Average non-cached time "
- ;; (if (eq? *number-non-write-queries* 0)
- ;; "n/a (no queries)"
- ;; (/ *total-non-write-delay*
- ;; *number-non-write-queries*))
- ;; " ms")
-
- (db:print-current-query-stats)
- (common:save-pkt `((action . exit)
- (T . server)
- (pid . ,(current-process-id)))
- *configdat* #t)
- (debug:print-info 0 *default-log-port* "Server shutdown complete. Exiting")
- (exit)))
-
-;; all routes though here end in exit ...
-;;
-;; start_server?
-;;
-(define (http-transport:launch)
- ;; check that a server start is in progress, pause or exit if so
- (let* ((tmp-area (common:get-db-tmp-area))
- (server-start (conc tmp-area "/.server-start"))
- (server-started (conc tmp-area "/.server-started"))
- (start-time (common:lazy-modification-time server-start))
- (started-time (common:lazy-modification-time server-started))
- (server-starting (< start-time started-time)) ;; if start-time is less than started-time then a server is still starting
- (start-time-old (> (- (current-seconds) start-time) 5))
- (cleanup-proc (lambda (msg)
- (let* ((serv-fname (conc "server-" (current-process-id) "-" (get-host-name) ".log"))
- (full-serv-fname (conc *toppath* "/logs/" serv-fname))
- (new-serv-fname (conc *toppath* "/logs/" "defunct-" serv-fname)))
- (debug:print 0 *default-log-port* msg)
- (if (common:file-exists? full-serv-fname)
- (system (conc "sleep 1;mv -f " full-serv-fname " " new-serv-fname))
- (debug:print 0 *default-log-port* "INFO: cannot move " full-serv-fname " to " new-serv-fname))
- (exit)))))
- #;(if (and (not start-time-old) ;; last server start try was less than five seconds ago
- (not server-starting))
- (begin
- (cleanup-proc "NOT starting server, there is either a recently started server or a server in process of starting")
- (exit)))
- ;; lets not even bother to start if there are already three or more server files ready to go
- #;(let* ((num-alive (server:get-num-alive (server:get-list *toppath*))))
- (if (> num-alive 3)
- (begin
- (cleanup-proc (conc "ERROR: Aborting server start because there are already " num-alive " possible servers either running or starting up"))
- (exit))))
- (common:save-pkt `((action . start)
- (T . server)
- (pid . ,(current-process-id)))
- *configdat* #t)
- (let* ((th2 (make-thread (lambda ()
- (debug:print-info 0 *default-log-port* "Server run thread started")
- (http-transport:run
- (if (args:get-arg "-server")
- (args:get-arg "-server")
- "-")
- )) "Server run"))
- (th3 (make-thread (lambda ()
- (debug:print-info 0 *default-log-port* "Server monitor thread started")
- (http-transport:keep-running)
- "Keep running"))))
- (thread-start! th2)
- (thread-sleep! 0.252) ;; give the server time to settle before starting the keep-running monitor.
- (thread-start! th3)
- (set! *didsomething* #t)
- (thread-join! th2)
- (exit))))
-
-;; (define (http-transport:server-signal-handler signum)
-;; (signal-mask! signum)
-;; (handle-exceptions
-;; exn
-;; (debug:print 0 *default-log-port* " ... exiting ...")
-;; (let ((th1 (make-thread (lambda ()
-;; (thread-sleep! 1))
-;; "eat response"))
-;; (th2 (make-thread (lambda ()
-;; (debug:print-error 0 *default-log-port* "Received ^C, attempting clean exit. Please be patient and wait a few seconds before hitting ^C again.")
-;; (thread-sleep! 3) ;; give the flush three seconds to do it's stuff
-;; (debug:print 0 *default-log-port* " Done.")
-;; (exit 4))
-;; "exit on ^C timer")))
-;; (thread-start! th2)
-;; (thread-start! th1)
-;; (thread-join! th2))))
-
-;;===============================================
-;; Java script
-;;===============================================
-(define (http-transport:show-jquery)
- (let* ((data (tests:readlines *java-script-lib*)))
-(string-join data "\n")))
-
-
-
-;;======================================================================
-;; web pages
-;;======================================================================
-
-(define (http-transport:html-test-log $)
- (let* ((run-id ($ 'runid))
- (test-item ($ 'testname))
- (parts (string-split test-item ":"))
- (test-name (car parts))
-
- (item-name (if (equal? (length parts) 1)
- ""
- (cadr parts))))
- ;(print $)
-(tests:get-test-log run-id test-name item-name)))
-
-
-(define (http-transport:html-dboard $)
- (let* ((page ($ 'page))
- (oup (open-output-string))
- (bdy "--------------------------")
-
- (ret (tests:dynamic-dboard page)))
- (s:output-new oup ret)
- (close-output-port oup)
-
- (set! bdy (get-output-string oup))
- (conc "
Dashboard
" bdy "
" )))
-
-(define (http-transport:main-page)
- (let ((linkpath (root-path)))
- (conc "" (pathname-strip-directory *toppath*) "
"
- ""
- "Run area: " *toppath*
- "Server Stats
"
- (http-transport:stats-table)
- "
"
- (http-transport:runs linkpath)
- "
"
- ;; (http-transport:run-stats)
- ""
- )))
-
-(define (http-transport:stats-table)
- (mutex-lock! *heartbeat-mutex*)
- (let ((res
- (conc ""
- ;; "Max cached queries | " *max-cache-size* " |
"
- "Number of cached writes | " *number-of-writes* " |
"
- "Average cached write time | " (if (eq? *number-of-writes* 0)
- "n/a (no writes)"
- (/ *writes-total-delay*
- *number-of-writes*))
- " ms |
"
- "Number non-cached queries | " *number-non-write-queries* " |
"
- ;; "Average non-cached time | " (if (eq? *number-non-write-queries* 0)
- ;; "n/a (no queries)"
- ;; (/ *total-non-write-delay*
- ;; *number-non-write-queries*))
- " ms |
"
- "Last access | " (seconds->time-string *db-last-access*) " |
"
- "
")))
- (mutex-unlock! *heartbeat-mutex*)
- res))
-
-(define (http-transport:runs linkpath)
- (conc "Runs
"
- (string-intersperse
- (let ((files (map pathname-strip-directory (glob (conc linkpath "/*")))))
- (map (lambda (p)
- (conc "" p "
"))
- files))
- " ")))
-
-#;(define (http-transport:run-stats)
- (let ((stats (open-run-close db:get-running-stats #f)))
- (conc ""
- (string-intersperse
- (map (lambda (stat)
- (conc "" (car stat) " | " (cadr stat) " |
"))
- stats)
- " ")
- "
")))
ADDED http-transportmod.scm
Index: http-transportmod.scm
==================================================================
--- /dev/null
+++ http-transportmod.scm
@@ -0,0 +1,795 @@
+;;======================================================================
+;; Copyright 2017, Matthew Welland.
+;;
+;; This file is part of Megatest.
+;;
+;; Megatest is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; Megatest is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with Megatest. If not, see .
+
+;;======================================================================
+
+(declare (unit http-transportmod))
+(declare (uses debugprint))
+(declare (uses commonmod))
+(declare (uses configfmod))
+(declare (uses mtargs))
+(declare (uses mtver))
+(declare (uses dbmod))
+(declare (uses stml2))
+(declare (uses portloggermod))
+
+(module http-transportmod
+ *
+
+(import scheme
+ (prefix sqlite3 sqlite3:)
+ chicken.base
+ chicken.condition
+ chicken.file
+ chicken.file.posix
+ chicken.format
+ chicken.io
+ chicken.pathname
+ chicken.port
+ chicken.pretty-print
+ chicken.process
+ chicken.process-context
+ chicken.process-context.posix
+ chicken.sort
+ chicken.string
+ chicken.time
+ chicken.time.posix
+ chicken.tcp
+
+ (prefix base64 base64:)
+ directory-utils
+ matchable
+ md5
+ message-digest
+ regex
+ s11n
+ srfi-1
+ srfi-13
+ srfi-18
+ srfi-69
+ stack
+ system-information
+ typed-records
+ z3
+ spiffy
+ uri-common
+ intarweb
+ http-client
+ spiffy-request-vars
+ intarweb
+ spiffy-directory-listing
+
+ (prefix mtargs args:)
+ commonmod
+ configfmod
+ debugprint
+ mtver
+ dbmod
+ stml2
+ portloggermod
+
+ )
+
+;; (require-extension (srfi 18) extras tcp s11n)
+;;
+;;
+;; (use srfi-1 posix regex regex-case srfi-69 hostinfo md5 message-digest posix-extras)
+;;
+;; (use spiffy uri-common intarweb http-client spiffy-request-vars intarweb spiffy-directory-listing)
+;;
+;; Configurations for server
+(tcp-buffer-size 2048)
+(max-connections 2048)
+
+;; (declare (unit http-transport))
+;;
+;; (declare (uses common))
+;; (declare (uses db))
+;; (declare (uses tests))
+;; (declare (uses tasks)) ;; tasks are where stuff is maintained about what is running.
+;; (declare (uses server))
+;; ;; (declare (uses daemon))
+;; (declare (uses portlogger))
+;; (declare (uses rmt))
+;;
+;; (include "common_records.scm")
+(include "db_records.scm")
+;; (include "js-path.scm")
+
+;; (require-library stml)
+(define (http-transport:make-server-url hostport)
+ (if (not hostport)
+ #f
+ (conc "http://" (car hostport) ":" (cadr hostport))))
+
+;;======================================================================
+;; S E R V E R
+;; ======================================================================
+
+;; Call this to start the actual server
+;;
+
+(define *db:process-queue-mutex* (make-mutex))
+
+(define (http-transport:run hostn)
+ ;; Configurations for server
+ (tcp-buffer-size 2048)
+ (max-connections 2048)
+ (debug:print 2 *default-log-port* "Attempting to start the server ...")
+ (let* ((db #f) ;; (open-db)) ;; we don't want the server to be opening and closing the db unnecesarily
+ (hostname (get-host-name))
+ (ipaddrstr (let ((ipstr (if (string=? "-" hostn)
+ ;; (string-intersperse (map number->string (u8vector->list (hostname->ip hostname))) ".")
+ (server:get-best-guess-address hostname)
+ #f)))
+ (if ipstr ipstr hostn))) ;; hostname)))
+ (start-port (portlogger:open-run-close portlogger:find-port))
+ (link-tree-path (common:get-linktree))
+ (tmp-area (common:get-db-tmp-area))
+ (start-file (conc tmp-area "/.server-start")))
+ (debug:print-info 0 *default-log-port* "portlogger recommended port: " start-port)
+ ;; set some parameters for the server
+ (root-path (if link-tree-path
+ link-tree-path
+ (current-directory))) ;; WARNING: SECURITY HOLE. FIX ASAP!
+ (handle-directory spiffy-directory-listing)
+ (handle-exception (lambda (exn chain)
+ (signal (make-composite-condition
+ (make-property-condition
+ 'server
+ 'message "server error")))))
+
+ ;; http-transport:handle-directory) ;; simple-directory-handler)
+ ;; Setup the web server and a /ctrl interface
+ ;;
+ (vhost-map `(((* any) . ,(lambda (continue)
+ ;; open the db on the first call
+ ;; This is were we set up the database connections
+ (let* (($ (request-vars source: 'both))
+ (dat ($ 'dat))
+ (res #f))
+ (cond
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ "api"))
+ (send-response body: (api:process-request *dbstruct-db* $) ;; the $ is the request vars proc
+ headers: '((content-type text/plain)))
+ (mutex-lock! *heartbeat-mutex*)
+ (set! *db-last-access* (current-seconds))
+ (mutex-unlock! *heartbeat-mutex*))
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ ""))
+ (send-response body: (http-transport:main-page)))
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ "json_api"))
+ (send-response body: (http-transport:main-page)))
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ "runs"))
+ (send-response body: (http-transport:main-page)))
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ any))
+ (send-response body: "hey there!\n"
+ headers: '((content-type text/plain))))
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ "hey"))
+ (send-response body: "hey there!\n"
+ headers: '((content-type text/plain))))
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ "jquery3.1.0.js"))
+ (send-response body: (http-transport:show-jquery)
+ headers: '((content-type application/javascript))))
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ "test_log"))
+ (send-response body: (http-transport:html-test-log $)
+ headers: '((content-type text/HTML))))
+ ((equal? (uri-path (request-uri (current-request)))
+ '(/ "dashboard"))
+ (send-response body: (http-transport:html-dboard $)
+ headers: '((content-type text/HTML))))
+ (else (continue))))))))
+ (handle-exceptions
+ exn
+ (debug:print 0 *default-log-port* "Failed to create file " start-file ", exn=" exn)
+ (with-output-to-file start-file (lambda ()(print (current-process-id)))))
+ (http-transport:try-start-server ipaddrstr start-port)))
+
+;; This is recursively run by http-transport:run until sucessful
+;;
+(define (http-transport:try-start-server ipaddrstr portnum)
+ (let ((config-hostname (configf:lookup *configdat* "server" "hostname"))
+ (config-use-proxy (equal? (configf:lookup *configdat* "client" "use-http_proxy") "yes")))
+ (if (not config-use-proxy)
+ (determine-proxy (constantly #f)))
+ (debug:print-info 0 *default-log-port* "http-transport:try-start-server time=" (seconds->time-string (current-seconds)) " ipaddrsstr=" ipaddrstr " portnum=" portnum " config-hostname=" config-hostname)
+ (handle-exceptions
+ exn
+ (begin
+ (print-error-message exn)
+ (if (< portnum 64000)
+ (begin
+ (debug:print 0 *default-log-port* "WARNING: attempt to start server failed. Trying again ...")
+ (debug:print 0 *default-log-port* " message: " ((condition-property-accessor 'exn 'message) exn))
+ (debug:print 5 *default-log-port* "exn=" (condition->list exn))
+ (portlogger:open-run-close portlogger:set-failed portnum)
+ (debug:print 0 *default-log-port* "WARNING: failed to start on portnum: " portnum ", trying next port")
+ (thread-sleep! 0.1)
+
+ ;; get_next_port goes here
+ (http-transport:try-start-server ipaddrstr
+ (portlogger:open-run-close portlogger:find-port)))
+ (begin
+ (print "ERROR: Tried and tried but could not start the server"))))
+ ;; any error in following steps will result in a retry
+ (set! *server-info* (list ipaddrstr portnum))
+ (debug:print 0 *default-log-port* "INFO: Trying to start server on " ipaddrstr ":" portnum)
+ ;; This starts the spiffy server
+ ;; NEED WAY TO SET IP TO #f TO BIND ALL
+ ;; (start-server bind-address: ipaddrstr port: portnum)
+ (if config-hostname ;; this is a hint to bind directly
+ (start-server port: portnum bind-address: (if (equal? config-hostname "-")
+ ipaddrstr
+ config-hostname))
+ (start-server port: portnum))
+ (portlogger:open-run-close portlogger:set-port portnum "released")
+ (debug:print 1 *default-log-port* "INFO: server has been stopped"))))
+
+;;======================================================================
+;; S E R V E R U T I L I T I E S
+;;======================================================================
+
+;;======================================================================
+;; C L I E N T S
+;;======================================================================
+
+(define *http-mutex* (make-mutex))
+
+;; NOTE: Large block of code from 32436b426188080f72fceb6894af541fbad9921e removed here
+;; I'm pretty sure it is defunct.
+
+;; This next block all imported en-mass from the api branch
+(define *http-requests-in-progress* 0)
+(define *http-connections-next-cleanup* (current-seconds))
+
+(define (http-transport:get-time-to-cleanup)
+ (let ((res #f))
+ (mutex-lock! *http-mutex*)
+ (set! res (> (current-seconds) *http-connections-next-cleanup*))
+ (mutex-unlock! *http-mutex*)
+ res))
+
+(define (http-transport:inc-requests-count)
+ (mutex-lock! *http-mutex*)
+ (set! *http-requests-in-progress* (+ 1 *http-requests-in-progress*))
+ ;; Use this opportunity to slow things down iff there are too many requests in flight
+ (if (> *http-requests-in-progress* 5)
+ (begin
+ (debug:print-info 0 *default-log-port* "Whoa there buddy, ease up...")
+ (thread-sleep! 1)))
+ (mutex-unlock! *http-mutex*))
+
+(define (http-transport:dec-requests-count proc)
+ (mutex-lock! *http-mutex*)
+ (proc)
+ (set! *http-requests-in-progress* (- *http-requests-in-progress* 1))
+ (mutex-unlock! *http-mutex*))
+
+(define (http-transport:dec-requests-count-and-close-all-connections)
+ (set! *http-requests-in-progress* (- *http-requests-in-progress* 1))
+ (let loop ((etime (+ (current-seconds) 5))) ;; give up in five seconds
+ (if (> *http-requests-in-progress* 0)
+ (if (> etime (current-seconds))
+ (begin
+ (thread-sleep! 0.052)
+ (loop etime))
+ (debug:print-error 0 *default-log-port* "requests still in progress after 5 seconds of waiting. I'm going to pass on cleaning up http connections"))
+ (close-idle-connections!)))
+ (set! *http-connections-next-cleanup* (+ (current-seconds) 10))
+ (mutex-unlock! *http-mutex*))
+
+(define (http-transport:inc-requests-and-prep-to-close-all-connections)
+ (mutex-lock! *http-mutex*)
+ (set! *http-requests-in-progress* (+ 1 *http-requests-in-progress*)))
+
+;; Send "cmd" with json payload "params" to serverdat and receive result
+;;
+(define (http-transport:client-api-send-receive run-id serverdat cmd params #!key (numretries 3)(area-dat #f))
+ (let* ((fullurl (if (vector? serverdat)
+ (http-transport:server-dat-get-api-req serverdat)
+ (begin
+ (debug:print 0 *default-log-port* "FATAL ERROR: http-transport:client-api-send-receive called with no server info")
+ (exit 1))))
+ (res (vector #f "uninitialized"))
+ (success #t)
+ (sparams (db:obj->string params transport: 'http))
+ (runremote (or area-dat *runremote*))
+ (server-id (if (vector? serverdat)
+ (http-transport:server-dat-get-server-id serverdat)
+ (begin
+ (debug:print 0 *default-log-port* "FATAL ERROR: http-transport:client-api-send-receive called with no server info")
+ (exit 1)))))
+ (debug:print-info 11 *default-log-port* "cmd=" cmd " fullurl=" fullurl " server-id=" server-id " current time:" (current-seconds))
+
+ ;; set up the http-client here
+ (max-retry-attempts 1)
+ ;; consider all requests indempotent
+ (retry-request? (lambda (request)
+ #f))
+ ;; send the data and get the response
+ ;; extract the needed info from the http data and
+ ;; process and return it.
+ (let* ((send-recieve (lambda ()
+ (mutex-lock! *http-mutex*)
+ ;; (condition-case (with-input-from-request "http://localhost"; #f read-lines)
+ ;; ((exn http client-error) e (print e)))
+ (set! res (vector ;;; DON'T FORGET - THIS IS THE CLIENT SIDE! NOTE: consider moving this to client.scm since we are only supporting http transport at this time.
+ success
+ (db:string->obj
+ (handle-exceptions
+ exn
+ (let ((call-chain (get-call-chain))
+ (msg ((condition-property-accessor 'exn 'message) exn)))
+ (set! success #f)
+ (if (debug:debug-mode 1)
+ (debug:print-info 0 *default-log-port* "couldn't talk to server, trying again ...")
+ (begin
+ (debug:print 0 *default-log-port* "WARNING: failure in with-input-from-request to " fullurl ".")
+ (debug:print 0 *default-log-port* " message: " msg ", exn=" exn)
+ (debug:print 0 *default-log-port* " cmd: " cmd " params: " params " key:" (or server-id "thekey"))
+ (debug:print 0 *default-log-port* " call-chain: " call-chain)))
+ (if runremote
+ (remote-conndat-set! runremote #f))
+ ;; Killing associated server to allow clean retry.")
+ ;; (tasks:kill-server-run-id run-id) ;; better to kill the server in the logic that called this routine?
+ (mutex-unlock! *http-mutex*)
+ ;;; (signal (make-composite-condition
+ ;;; (make-property-condition 'commfail 'message "failed to connect to server")))
+ ;;; "communications failed"
+ (db:obj->string #f))
+ (with-input-from-request ;; was dat
+ fullurl
+ (list (cons 'key (or server-id "thekey"))
+ (cons 'cmd cmd)
+ (cons 'params sparams))
+ read-string))
+ transport: 'http)
+ 0)) ;; added this speculatively
+ ;; Shouldn't this be a call to the managed call-all-connections stuff above?
+ (close-idle-connections!)
+ (mutex-unlock! *http-mutex*)
+ ))
+ (time-out (lambda ()
+ (thread-sleep! 45)
+ (debug:print 0 *default-log-port* "WARNING: send-receive took more than 45 seconds!!")
+ #f))
+ (th1 (make-thread send-recieve "with-input-from-request"))
+ (th2 (make-thread time-out "time out")))
+ (thread-start! th1)
+ (thread-start! th2)
+ (thread-join! th1)
+ (vector-set! res 0 success)
+ (thread-terminate! th2)
+ (if (vector? res)
+ (if (vector-ref res 0) ;; this is the first flag or the second flag?
+ (let* ((res-dat (vector-ref res 1)))
+ (if (and (string? res-dat) (string-contains res-dat "server-id mismatch"))
+ (signal (make-composite-condition
+ (make-property-condition
+ 'servermismatch
+ 'message (vector-ref res 1))))
+ res)) ;; this is the *inner* vector? seriously? why?
+ (if (debug:debug-mode 11)
+ (let ((call-chain (get-call-chain))) ;; note: this code also called in nmsg-transport - consider consolidating it
+ (print-call-chain (current-error-port))
+ (debug:print-error 11 *default-log-port* "error above occured at server, res=" res) ;; " message: " ((condition-property-accessor 'exn 'message) exn))
+ (debug:print 11 *default-log-port* " server call chain:")
+ (pp (vector-ref res 1) (current-error-port))
+ (signal (vector-ref res 0)))
+ res))
+ (signal (make-composite-condition
+ (make-property-condition
+ 'timeout
+ 'message "nmsg-transport:client-api-send-receive-raw timed out talking to server")))))))
+
+;; careful closing of connections stored in *runremote*
+;;
+(define (http-transport:close-connections #!key (area-dat #f))
+ (let* ((runremote (or area-dat *runremote*))
+ (server-dat (if runremote
+ (remote-conndat runremote)
+ #f))) ;; (hash-table-ref/default *runremote* run-id #f)))
+ (if (vector? server-dat)
+ (let ((api-dat (http-transport:server-dat-get-api-uri server-dat)))
+ (handle-exceptions
+ exn
+ (begin
+ (print-call-chain *default-log-port*)
+ (debug:print-error 0 *default-log-port* " closing connection failed with error: " ((condition-property-accessor 'exn 'message) exn) ", exn=" exn))
+ (close-connection! api-dat)
+ ;;(close-idle-connections!)
+ #t))
+ #f)))
+
+
+(define (make-http-transport:server-dat)(make-vector 6))
+(define (http-transport:server-dat-get-iface vec) (vector-ref vec 0))
+(define (http-transport:server-dat-get-port vec) (vector-ref vec 1))
+(define (http-transport:server-dat-get-api-uri vec) (vector-ref vec 2))
+(define (http-transport:server-dat-get-api-url vec) (vector-ref vec 3))
+(define (http-transport:server-dat-get-api-req vec) (vector-ref vec 4))
+(define (http-transport:server-dat-get-last-access vec) (vector-ref vec 5))
+;(define (http-transport:server-dat-get-socket vec) (vector-ref vec 6))
+(define (http-transport:server-dat-get-server-id vec) (vector-ref vec 6))
+
+(define (http-transport:server-dat-make-url vec)
+ (if (and (http-transport:server-dat-get-iface vec)
+ (http-transport:server-dat-get-port vec))
+ (conc "http://"
+ (http-transport:server-dat-get-iface vec)
+ ":"
+ (http-transport:server-dat-get-port vec))
+ #f))
+
+(define (http-transport:server-dat-update-last-access vec)
+ (if (vector? vec)
+ (vector-set! vec 5 (current-seconds))
+ (begin
+ (print-call-chain (current-error-port))
+ (debug:print-error 0 *default-log-port* "call to http-transport:server-dat-update-last-access with non-vector!!"))))
+
+;;
+;; connect
+;;
+(define (http-transport:client-connect iface port server-id)
+ (let* ((api-url (conc "http://" iface ":" port "/api"))
+ (api-uri (uri-reference (conc "http://" iface ":" port "/api")))
+ (api-req (make-request method: 'POST uri: api-uri))
+ (server-dat (vector iface port api-uri api-url api-req (current-seconds) server-id)))
+ server-dat))
+
+;; run http-transport:keep-running in a parallel thread to monitor that the db is being
+;; used and to shutdown after sometime if it is not.
+;;
+(define (http-transport:keep-running)
+ ;; if none running or if > 20 seconds since
+ ;; server last used then start shutdown
+ ;; This thread waits for the server to come alive
+ (debug:print-info 0 *default-log-port* "Starting the sync-back, keep alive thread in server")
+ (let* ((sdat #f)
+ (tmp-area (common:get-db-tmp-area))
+ (started-file (conc tmp-area "/.server-started"))
+ (server-start-time (current-seconds))
+ (server-info (let loop ((start-time (current-seconds))
+ (changed #t)
+ (last-sdat "not this"))
+ (begin ;; let ((sdat #f))
+ (thread-sleep! 0.01)
+ (debug:print-info 0 *default-log-port* "Waiting for server alive signature")
+ (mutex-lock! *heartbeat-mutex*)
+ (set! sdat *server-info*)
+ (mutex-unlock! *heartbeat-mutex*)
+ (if (and sdat
+ (not changed)
+ (> (- (current-seconds) start-time) 2))
+ (begin
+ (debug:print-info 0 *default-log-port* "Received server alive signature")
+ (common:save-pkt `((action . alive)
+ (T . server)
+ (pid . ,(current-process-id))
+ (ipaddr . ,(car sdat))
+ (port . ,(cadr sdat)))
+ *configdat* #t)
+ sdat)
+ (begin
+ (debug:print-info 0 *default-log-port* "Still waiting, last-sdat=" last-sdat)
+ (sleep 4)
+ (if (> (- (current-seconds) start-time) 120) ;; been waiting for two minutes
+ (begin
+ (debug:print-error 0 *default-log-port* "transport appears to have died, exiting server")
+ (common:save-pkt `((action . died)
+ (T . server)
+ (pid . ,(current-process-id))
+ (ipaddr . ,(car sdat))
+ (port . ,(cadr sdat))
+ (msg . "Transport died?"))
+ *configdat* #t)
+ (exit))
+ (loop start-time
+ (equal? sdat last-sdat)
+ sdat)))))))
+ (iface (car server-info))
+ (port (cadr server-info))
+ (last-access 0)
+ (server-timeout (server:expiration-timeout))
+ (server-going #f)
+ (server-log-file (args:get-arg "-log"))) ;; always set when we are a server
+
+ (handle-exceptions
+ exn
+ (debug:print 0 *default-log-port* "Failed to create " started-file ", exn=" exn)
+ (with-output-to-file started-file (lambda ()(print (current-process-id)))))
+
+ (let loop ((count 0)
+ (server-state 'available)
+ (bad-sync-count 0)
+ (start-time (current-milliseconds)))
+ ;; Use this opportunity to sync the tmp db to megatest.db
+ (if (not server-going) ;; *dbstruct-db*
+ (begin
+ (debug:print 0 *default-log-port* "SERVER: dbprep")
+ (set! *dbstruct-db* (db:setup #t)) ;; run-id))
+ (set! server-going #t)
+ (debug:print 0 *default-log-port* "SERVER: running, megatest version: " (common:get-full-version)) ;; NOTE: the server is NOT yet marked as running in the log. We do that in the keep-running routine.
+ (if *watchdog*
+ (thread-start! *watchdog*)
+ (debug:print 0 *default-log-port* "ERROR: *watchdog* not setup, cannot start it."))))
+
+ ;; when things go wrong we don't want to be doing the various queries too often
+ ;; so we strive to run this stuff only every four seconds or so.
+ (let* ((sync-time (- (current-milliseconds) start-time))
+ (rem-time (quotient (- 4000 sync-time) 1000)))
+ (if (and (<= rem-time 4)
+ (> rem-time 0))
+ (thread-sleep! rem-time)))
+
+ (if (< count 1) ;; 3x3 = 9 secs aprox
+ (loop (+ count 1) 'running bad-sync-count (current-milliseconds)))
+
+ ;; Check that iface and port have not changed (can happen if server port collides)
+ (mutex-lock! *heartbeat-mutex*)
+ (set! sdat *server-info*)
+ (mutex-unlock! *heartbeat-mutex*)
+
+ (if (not (equal? sdat (list iface port)))
+ (let ((new-iface (car sdat))
+ (new-port (cadr sdat)))
+ (debug:print-info 0 *default-log-port* "WARNING: interface changed, refreshing iface and port info")
+ (set! iface new-iface)
+ (set! port new-port)
+ (if (not *server-id*)
+ (set! *server-id* (server:mk-signature)))
+ (debug:print 0 *default-log-port* (current-seconds) (current-directory) (current-process-id) (argv))
+ (debug:print 0 *default-log-port* "SERVER STARTED: " iface ":" port " AT " (current-seconds) " server-id: " *server-id*)
+ (flush-output *default-log-port*)))
+
+ ;; Transfer *db-last-access* to last-access to use in checking that we are still alive
+ (mutex-lock! *heartbeat-mutex*)
+ (set! last-access *db-last-access*)
+ (mutex-unlock! *heartbeat-mutex*)
+
+ (if (common:low-noise-print 120 (conc "server running on " iface ":" port))
+ (begin
+ (if (not *server-id*)
+ (set! *server-id* (server:mk-signature)))
+ (debug:print 0 *default-log-port* (current-seconds) (current-directory) (current-process-id) (argv))
+ (debug:print 0 *default-log-port* "SERVER STARTED: " iface ":" port " AT " (current-seconds) " server-id: " *server-id*)
+ (flush-output *default-log-port*)))
+ (if (common:low-noise-print 60 "dbstats")
+ (begin
+ (debug:print 0 *default-log-port* "Server stats:")
+ (db:print-current-query-stats)))
+ (let* ((hrs-since-start (/ (- (current-seconds) server-start-time) 3600)))
+ (cond
+ ((and *server-run*
+ (> (+ last-access server-timeout)
+ (current-seconds)))
+ (if (common:low-noise-print 120 "server continuing")
+ (debug:print-info 0 *default-log-port* "Server continuing, seconds since last db access: " (- (current-seconds) last-access))
+ (let ((curr-time (current-seconds)))
+ (handle-exceptions
+ exn
+ (debug:print 0 *default-log-port* "ERROR: Failed to change timestamp on log file " server-log-file ". Are you out of space on that disk? exn=" exn)
+ (if (not *server-overloaded*)
+ (set-file-times! server-log-file curr-time curr-time)))))
+ (loop 0 server-state bad-sync-count (current-milliseconds)))
+ (else
+ (debug:print-info 0 *default-log-port* "Server timed out. seconds since last db access: " (- (current-seconds) last-access))
+ (http-transport:server-shutdown port)))))))
+
+(define (http-transport:server-shutdown port)
+ (begin
+ ;;(BB> "http-transport:server-shutdown called")
+ (debug:print-info 0 *default-log-port* "Starting to shutdown the server. pid="(current-process-id))
+ ;;
+ ;; start_shutdown
+ ;;
+ (set! *time-to-exit* #t) ;; tell on-exit to be fast as we've already cleaned up
+ (portlogger:open-run-close portlogger:set-port port "released")
+ (thread-sleep! 1)
+
+ ;; (debug:print-info 0 *default-log-port* "Max cached queries was " *max-cache-size*)
+ ;; (debug:print-info 0 *default-log-port* "Number of cached writes " *number-of-writes*)
+ ;; (debug:print-info 0 *default-log-port* "Average cached write time "
+ ;; (if (eq? *number-of-writes* 0)
+ ;; "n/a (no writes)"
+ ;; (/ *writes-total-delay*
+ ;; *number-of-writes*))
+ ;; " ms")
+ ;; (debug:print-info 0 *default-log-port* "Number non-cached queries " *number-non-write-queries*)
+ ;; (debug:print-info 0 *default-log-port* "Average non-cached time "
+ ;; (if (eq? *number-non-write-queries* 0)
+ ;; "n/a (no queries)"
+ ;; (/ *total-non-write-delay*
+ ;; *number-non-write-queries*))
+ ;; " ms")
+
+ (db:print-current-query-stats)
+ (common:save-pkt `((action . exit)
+ (T . server)
+ (pid . ,(current-process-id)))
+ *configdat* #t)
+ (debug:print-info 0 *default-log-port* "Server shutdown complete. Exiting")
+ (exit)))
+
+;; all routes though here end in exit ...
+;;
+;; start_server?
+;;
+(define (http-transport:launch)
+ ;; check that a server start is in progress, pause or exit if so
+ (let* ((tmp-area (common:get-db-tmp-area))
+ (server-start (conc tmp-area "/.server-start"))
+ (server-started (conc tmp-area "/.server-started"))
+ (start-time (common:lazy-modification-time server-start))
+ (started-time (common:lazy-modification-time server-started))
+ (server-starting (< start-time started-time)) ;; if start-time is less than started-time then a server is still starting
+ (start-time-old (> (- (current-seconds) start-time) 5))
+ (cleanup-proc (lambda (msg)
+ (let* ((serv-fname (conc "server-" (current-process-id) "-" (get-host-name) ".log"))
+ (full-serv-fname (conc *toppath* "/logs/" serv-fname))
+ (new-serv-fname (conc *toppath* "/logs/" "defunct-" serv-fname)))
+ (debug:print 0 *default-log-port* msg)
+ (if (common:file-exists? full-serv-fname)
+ (system (conc "sleep 1;mv -f " full-serv-fname " " new-serv-fname))
+ (debug:print 0 *default-log-port* "INFO: cannot move " full-serv-fname " to " new-serv-fname))
+ (exit)))))
+ #;(if (and (not start-time-old) ;; last server start try was less than five seconds ago
+ (not server-starting))
+ (begin
+ (cleanup-proc "NOT starting server, there is either a recently started server or a server in process of starting")
+ (exit)))
+ ;; lets not even bother to start if there are already three or more server files ready to go
+ #;(let* ((num-alive (server:get-num-alive (server:get-list *toppath*))))
+ (if (> num-alive 3)
+ (begin
+ (cleanup-proc (conc "ERROR: Aborting server start because there are already " num-alive " possible servers either running or starting up"))
+ (exit))))
+ (common:save-pkt `((action . start)
+ (T . server)
+ (pid . ,(current-process-id)))
+ *configdat* #t)
+ (let* ((th2 (make-thread (lambda ()
+ (debug:print-info 0 *default-log-port* "Server run thread started")
+ (http-transport:run
+ (if (args:get-arg "-server")
+ (args:get-arg "-server")
+ "-")
+ )) "Server run"))
+ (th3 (make-thread (lambda ()
+ (debug:print-info 0 *default-log-port* "Server monitor thread started")
+ (http-transport:keep-running)
+ "Keep running"))))
+ (thread-start! th2)
+ (thread-sleep! 0.252) ;; give the server time to settle before starting the keep-running monitor.
+ (thread-start! th3)
+ (set! *didsomething* #t)
+ (thread-join! th2)
+ (exit))))
+
+;; (define (http-transport:server-signal-handler signum)
+;; (signal-mask! signum)
+;; (handle-exceptions
+;; exn
+;; (debug:print 0 *default-log-port* " ... exiting ...")
+;; (let ((th1 (make-thread (lambda ()
+;; (thread-sleep! 1))
+;; "eat response"))
+;; (th2 (make-thread (lambda ()
+;; (debug:print-error 0 *default-log-port* "Received ^C, attempting clean exit. Please be patient and wait a few seconds before hitting ^C again.")
+;; (thread-sleep! 3) ;; give the flush three seconds to do it's stuff
+;; (debug:print 0 *default-log-port* " Done.")
+;; (exit 4))
+;; "exit on ^C timer")))
+;; (thread-start! th2)
+;; (thread-start! th1)
+;; (thread-join! th2))))
+
+
+
+
+;; called in megatest.scm, host-port is string hostname:port
+;;
+;; NOTE: This is NOT called directly from clients as not all transports support a client running
+;; in the same process as the server.
+;;
+(define (server:ping host-port-in server-id #!key (do-exit #f))
+ (let ((host:port (if (not host-port-in) ;; use read-dotserver to find
+ #f ;; (server:check-if-running *toppath*)
+ ;; (if (number? host-port-in) ;; we were handed a server-id
+ ;; (let ((srec (tasks:get-server-by-id (db:delay-if-busy (tasks:open-db)) host-port-in)))
+ ;; ;; (print "srec: " srec " host-port-in: " host-port-in)
+ ;; (if srec
+ ;; (conc (vector-ref srec 3) ":" (vector-ref srec 4))
+ ;; (conc "no such server-id " host-port-in)))
+ host-port-in))) ;; )
+ (let* ((host-port (if host:port
+ (let ((slst (string-split host:port ":")))
+ (if (eq? (length slst) 2)
+ (list (car slst)(string->number (cadr slst)))
+ #f))
+ #f)))
+;; (toppath (launch:setup)))
+ ;; (print "host-port=" host-port)
+ (if (not host-port)
+ (begin
+ (if host-port-in
+ (debug:print 0 *default-log-port* "ERROR: bad host:port"))
+ (if do-exit (exit 1))
+ #f)
+ (let* ((iface (car host-port))
+ (port (cadr host-port))
+ (server-dat (http-transport:client-connect iface port server-id))
+ (login-res (rmt:login-no-auto-client-setup server-dat)))
+ (if (and (list? login-res)
+ (car login-res))
+ (begin
+ ;; (print "LOGIN_OK")
+ (if do-exit (exit 0))
+ #t)
+ (begin
+ ;; (print "LOGIN_FAILED")
+ (if do-exit (exit 1))
+ #f)))))))
+
+;; This login does no retries under the hood - it acts a bit like a ping.
+;; Deprecated for nmsg-transport.
+;;
+(define (rmt:login-no-auto-client-setup connection-info)
+ (rmt:send-receive-no-auto-client-setup connection-info 'login 0 (list *toppath* megatest-version *my-client-signature*)))
+
+(define (rmt:send-receive-no-auto-client-setup connection-info cmd run-id params)
+ (let* ((run-id (if run-id run-id 0))
+ (res (handle-exceptions
+ exn
+ #f
+ (http-transport:client-api-send-receive run-id connection-info cmd params))))
+ (if (and res (vector-ref res 0))
+ (vector-ref res 1) ;;; YES!! THIS IS CORRECT!! CHANGE IT HERE, THEN CHANGE rmt:send-receive ALSO!!!
+ #f)))
+
+
+;; Get the transport
+(define (server:get-transport)
+ (if *transport-type*
+ *transport-type*
+ (let ((ttype (string->symbol
+ (or (args:get-arg "-transport")
+ (configf:lookup *configdat* "server" "transport")
+ "rpc"))))
+ (set! *transport-type* ttype)
+ ttype)))
+
+;; Generate a unique signature for this server
+(define (server:mk-signature)
+ (message-digest-string (md5-primitive)
+ (with-output-to-string
+ (lambda ()
+ (write (list (current-directory)
+ (current-process-id)
+ (argv)))))))
+
+
+)
Index: megatest.scm
==================================================================
--- megatest.scm
+++ megatest.scm
@@ -694,54 +694,10 @@
;; (not (null? (lset-intersection! eq? *verbosity* n))))
;; ((and (number? *verbosity*)
;; (list? n))
;; (member *verbosity* n))))
-;; TODO: for multiple areas, we will have multiple watchdogs; and multiple threads to manage
-(define *watchdog* (make-thread
- (lambda ()
- (handle-exceptions
- exn
- (begin
- (print-call-chain)
- (print " message: " ((condition-property-accessor 'exn 'message) exn) ", exn=" exn))
- (common:watchdog)))
- "Watchdog thread"))
-
- ;;(if (not (args:get-arg "-server"))
- ;; (thread-start! *watchdog*)) ;; if starting a server; wait till we get to running state before kicking off watchdog
- (let* ((no-watchdog-args
- '("-list-runs"
- "-testdata-csv"
- "-list-servers"
- "-server"
- "-adjutant"
- "-list-disks"
- "-list-targets"
- "-show-runconfig"
- ;;"-list-db-targets"
- "-show-runconfig"
- "-show-config"
- "-show-cmdinfo"
- "-cleanup-db"
- ))
- (no-watchdog-argvals (list '("-archive" . "replicate-db")))
- (start-watchdog-specail-arg-val (let loop ((hed (car no-watchdog-argvals))
- (tail (cdr no-watchdog-argvals)))
- ;; (print "hed" hed " arg " (args:get-arg (car hed)) " val:" (cdr hed) " eql" (equal? (args:get-arg (car hed)) (cdr hed)))
- (if (equal? (args:get-arg (car hed)) (cdr hed))
- #f
- (if (null? tail)
- #t
- (loop (car tail) (cdr tail))))))
- (no-watchdog-args-vals (filter (lambda (x) x)
- (map args:get-arg no-watchdog-args)))
- (start-watchdog (and (null? no-watchdog-args-vals) start-watchdog-specail-arg-val)))
- ;(print "no-watchdog-args="no-watchdog-args "no-watchdog-args-vals="no-watchdog-args-vals " start-watchdog-specail-arg-val:" start-watchdog-specail-arg-val " start-watchdog:" start-watchdog)
- (if start-watchdog
- (thread-start! *watchdog*)))
-
;; bracket open-output-file with code to make leading directory if it does not exist and handle exceptions
(define (open-logfile logpath-in)
(condition-case
(let* ((log-dir (or (pathname-directory logpath-in) "."))
Index: portlogger.scm
==================================================================
--- portlogger.scm
+++ portlogger.scm
@@ -23,166 +23,5 @@
;; (import (prefix sqlite3 sqlite3:))
;;
;; (declare (unit portlogger))
;; (declare (uses db))
-;; lsof -i
-
-(define (portlogger:open-db fname)
- (let* ((avail (tasks:wait-on-journal fname 5 remove: #t)) ;; wait up to about 10 seconds for the journal to go away
- (exists (common:file-exists? fname))
- (db (if avail
- (sqlite3:open-database fname)
- (begin
- (system (conc "rm -f " fname))
- (sqlite3:open-database fname))))
- (handler (sqlite3:make-busy-timeout 136000))
- (canwrite (file-writable? fname)))
- ;; (db-init (lambda ()
- ;; (sqlite3:execute
- ;; db
- ;; "CREATE TABLE IF NOT EXISTS ports (
- ;; port INTEGER PRIMARY KEY,
- ;; state TEXT DEFAULT 'not-used',
- ;; fail_count INTEGER DEFAULT 0,
- ;; update_time TIMESTAMP DEFAULT (strftime('%s','now')) );"))))
- (sqlite3:set-busy-handler! db handler)
- (db:set-sync db) ;; (sqlite3:execute db "PRAGMA synchronous = 0;")
- ;; (if (not exists) ;; needed with IF NOT EXISTS?
- (sqlite3:execute
- db
- "CREATE TABLE IF NOT EXISTS ports (
- port INTEGER PRIMARY KEY,
- state TEXT DEFAULT 'not-used',
- fail_count INTEGER DEFAULT 0,
- update_time TIMESTAMP DEFAULT (strftime('%s','now')) );")
- db))
-
-(define (portlogger:open-run-close proc . params)
- (let* ((fname (conc "/tmp/." (current-user-name) "-portlogger.db"))
- (avail (tasks:wait-on-journal fname 10))) ;; wait up to about 10 seconds for the journal to go away
- (handle-exceptions
- exn
- (begin
- ;; (release-dot-lock fname)
- (debug:print-error 0 *default-log-port* "portlogger:open-run-close failed. " proc " " params)
- (debug:print 0 *default-log-port* " message: " ((condition-property-accessor 'exn 'message) exn))
- (debug:print 5 *default-log-port* "exn=" (condition->list exn))
- (if (common:file-exists? fname)(delete-file fname)) ;; brutally get rid of it
- (print-call-chain (current-error-port)))
- (let* (;; (lock (obtain-dot-lock fname 2 9 10))
- (db (portlogger:open-db fname))
- (res (apply proc db params)))
- (sqlite3:finalize! db)
- ;; (release-dot-lock fname)
- res))))
-
-;; (fold-row PROC INIT DATABASE SQL . PARAMETERS)
-(define (portlogger:take-port db portnum)
- (let* ((qry1 (sqlite3:prepare db "INSERT INTO ports (port,state) VALUES (?,?);"))
- (qry2 (sqlite3:prepare db "UPDATE ports SET state=?,update_time=strftime('%s','now') WHERE port=?;"))
- (qry3 (sqlite3:prepare db "SELECT state FROM ports WHERE port=?;"))
- (res (sqlite3:with-transaction
- db
- (lambda ()
- ;; (fold-row (lambda (var curr) (or var curr)) #f db "SELECT var FROM foo WHERE id=100;")
- (let* ((curr #f)
- (res #f))
- (set! curr (sqlite3:fold-row
- (lambda (var curr)
- (or curr var curr))
- "not-tried"
- qry3
- portnum))
- ;; (print "curr=" curr)
- (set! res (case (string->symbol curr)
- ((released) (sqlite3:execute qry2 "taken" portnum) 'taken)
- ((not-tried) (sqlite3:execute qry1 portnum "taken") 'taken)
- ((taken) 'already-taken)
- ((failed) 'failed)
- (else 'error)))
- ;; (print "res=" res)
- res)))))
- (sqlite3:finalize! qry1)
- (sqlite3:finalize! qry2)
- (sqlite3:finalize! qry3)
- res))
-
-(define (portlogger:get-prev-used-port db)
- (handle-exceptions
- exn
- (begin
- (debug:print 0 *default-log-port* "EXCEPTION: portlogger database probably overloaded or unreadable. If you see this message again remove /tmp/.$USER-portlogger.db")
- (debug:print 0 *default-log-port* " message: " ((condition-property-accessor 'exn 'message) exn))
- (debug:print 5 *default-log-port* "exn=" (condition->list exn))
- (print-call-chain (current-error-port))
- (debug:print 0 *default-log-port* "Continuing anyway.")
- #f)
- (sqlite3:fold-row
- (lambda (var curr)
- (or curr var curr))
- #f
- db
- "SELECT (port) FROM ports WHERE state='released' LIMIT 1;")))
-
-(define (portlogger:find-port db)
- (let* ((lowport (let ((val (configf:lookup *configdat* "server" "lowport")))
- (if (and val
- (string->number val))
- (string->number val)
- 32768)))
- (portnum (or (portlogger:get-prev-used-port db)
- (+ lowport ;; top of registered ports is 49152 but lets use ports in the registered range
- (pseudo-random-integer (- 64000 lowport))))))
- (handle-exceptions
- exn
- (begin
- (debug:print 0 *default-log-port* "EXCEPTION: portlogger database probably overloaded or unreadable. If you see this message again remove /tmp/.$USER-portlogger.db")
- (debug:print 0 *default-log-port* " message: " ((condition-property-accessor 'exn 'message) exn))
- (debug:print 5 *default-log-port* "exn=" (condition->list exn))
- (print-call-chain (current-error-port))
- (debug:print 0 *default-log-port* "Continuing anyway."))
- (portlogger:take-port db portnum))
- portnum))
-
-;; set port to "released", "failed" etc.
-;;
-(define (portlogger:set-port db portnum value)
- (sqlite3:execute db "UPDATE ports SET state=?,update_time=strftime('%s','now') WHERE port=?;" value portnum))
-
-;; set port to failed (attempted to take but got error)
-;;
-(define (portlogger:set-failed db portnum)
- (sqlite3:execute db "UPDATE ports SET state='failed',fail_count=fail_count+1,update_time=strftime('%s','now') WHERE port=?;" portnum))
-
-;;======================================================================
-;; MAIN
-;;======================================================================
-
-(define (portlogger:main . args)
- (let* ((dbfname (conc "/tmp/." (current-user-name) "-portlogger.db"))
- (db (portlogger:open-db dbfname))
- (numargs (length args))
- (result
- (handle-exceptions
- exn
- (begin
- (debug:print 0 *default-log-port* "EXCEPTION: portlogger database at " dbfname " probably overloaded or unreadable. Try removing it.")
- (debug:print 0 *default-log-port* " message: " ((condition-property-accessor 'exn 'message) exn))
- (debug:print 5 *default-log-port* "exn=" (condition->list exn))
- (debug:print 0 *default-log-port* " status: " ((condition-property-accessor 'sqlite3 'status) exn))
- (print-call-chain (current-error-port))
- #f)
- (case (string->symbol (car args)) ;; commands with two or more params
- ((take)(portlogger:take-port db (string->number (cadr args))))
- ((find)(portlogger:find-port db))
- ((set) (let ((port (cadr args))
- (state (caddr args)))
- (portlogger:set-port db
- (if (number? port) port (string->number port))
- state)
- state))
- ((failed)(portlogger:set-failed db (string->number (cadr args))) 'failed)))))
- (sqlite3:finalize! db)
- result))
-
-;; (print (apply portlogger:main (cdr (argv))))
Index: rmtmod.scm
==================================================================
--- rmtmod.scm
+++ rmtmod.scm
@@ -476,26 +476,14 @@
;; (rmt:update-db-stats run-id cmd params duration)
;; mark this run as dirty if this was a write, the watchdog is responsible for syncing it
(if qry-is-write
(let ((start-time (current-seconds)))
(mutex-lock! *db-multi-sync-mutex*)
-/ (set! *db-last-access* start-time) ;; THIS IS PROBABLY USELESS? (we are on a client)
+ (set! *db-last-access* start-time) ;; THIS IS PROBABLY USELESS? (we are on a client)
(mutex-unlock! *db-multi-sync-mutex*)))))
res))
-(define (rmt:send-receive-no-auto-client-setup connection-info cmd run-id params)
- (let* ((run-id (if run-id run-id 0))
- (res (handle-exceptions
- exn
- (begin
- (print "transport failed. exn=" exn)
- #f)
- (http-transport:client-api-send-receive run-id connection-info cmd params))))
- (if (and res (vector-ref res 0))
- (vector-ref res 1) ;;; YES!! THIS IS CORRECT!! CHANGE IT HERE, THEN CHANGE rmt:send-receive ALSO!!!
- #f)))
-
;;======================================================================
;;
;; A C T U A L A P I C A L L S
;;
;;======================================================================
@@ -515,18 +503,12 @@
;;======================================================================
(define (rmt:login run-id)
(rmt:send-receive 'login run-id (list *toppath* megatest-version *my-client-signature*)))
-;; This login does no retries under the hood - it acts a bit like a ping.
-;; Deprecated for nmsg-transport.
-;;
-(define (rmt:login-no-auto-client-setup connection-info)
- (case *transport-type* ;; run-id of 0 is just a placeholder
- ((http)(rmt:send-receive-no-auto-client-setup connection-info 'login 0 (list *toppath* megatest-version *my-client-signature*)))
- ;;((nmsg)(nmsg-transport:client-api-send-receive run-id connection-info 'login (list *toppath* megatest-version run-id *my-client-signature*)))
- ))
+;; rmt:login-no-auto-client-setup
+;; rmt:send-receive-no-auto-client-setup
;; hand off a call to one of the db:queries statements
;; added run-id to make looking up the correct db possible
;;
(define (rmt:general-call stmtname run-id . params)
Index: runsmod.scm
==================================================================
--- runsmod.scm
+++ runsmod.scm
@@ -28,10 +28,11 @@
(declare (uses keysmod))
(declare (uses mtmod))
(declare (uses processmod))
(declare (uses dbmod))
(declare (uses rmtmod))
+(declare (uses testsmod))
(module runsmod
*
(import scheme
@@ -76,10 +77,11 @@
mtmod
mtver
processmod
dbmod
rmtmod
+ testsmod
)
;; use this struct to facilitate refactoring
;;
Index: server.scm
==================================================================
--- server.scm
+++ server.scm
@@ -69,30 +69,10 @@
;;======================================================================
;; S E R V E R U T I L I T I E S
;;======================================================================
-;; Get the transport
-(define (server:get-transport)
- (if *transport-type*
- *transport-type*
- (let ((ttype (string->symbol
- (or (args:get-arg "-transport")
- (configf:lookup *configdat* "server" "transport")
- "rpc"))))
- (set! *transport-type* ttype)
- ttype)))
-
-;; Generate a unique signature for this server
-(define (server:mk-signature)
- (message-digest-string (md5-primitive)
- (with-output-to-string
- (lambda ()
- (write (list (current-directory)
- (current-process-id)
- (argv)))))))
-
;; When using zmq this would send the message back (two step process)
;; with spiffy or rpc this simply returns the return data to be returned
;;
(define (server:reply return-addr query-sig success/fail result)
(debug:print-info 11 *default-log-port* "server:reply return-addr=" return-addr ", result=" result)
@@ -174,20 +154,10 @@
(lambda (toppath)
(set! *db-last-access* (current-seconds)) ;; might not be needed.
(if (equal? *toppath* toppath)
#t
#f)))
-
-;; timeout is hms string: 1h 5m 3s, default is 1 minute
-;;
-(define (server:expiration-timeout)
- (let ((tmo (configf:lookup *configdat* "server" "timeout")))
- (if (and (string? tmo)
- (common:hms-string->seconds tmo)) ;; BUG: hms-string->seconds is broken, if given "10" returns 0. Also, it doesn't belong in this logic unless the string->number is changed below
- (* 3600 (string->number tmo))
- 60)))
-
;; (define server:sync-lock-token "SERVER_SYNC_LOCK")
;; (define (server:release-sync-lock)
;; (db:no-sync-del! *no-sync-db* server:sync-lock-token))
;; (define (server:have-sync-lock?)
Index: servermod.scm
==================================================================
--- servermod.scm
+++ servermod.scm
@@ -20,10 +20,11 @@
(declare (unit servermod))
(declare (uses commonmod))
(declare (uses debugprint))
(declare (uses configfmod))
+(declare (uses http-transportmod))
(module servermod
*
(import scheme
@@ -49,10 +50,11 @@
srfi-69
commonmod
debugprint
configfmod
+ http-transportmod
)
(define (server:make-server-url hostport)
(if (not hostport)
@@ -288,52 +290,8 @@
(debug:print-info 0 *default-log-port* "Gating server start, last start: "
fmodtime ", delta: " delta ", reftime: " reftime ", all-go=" all-go)
(thread-sleep! reftime)
(server:wait-for-server-start-last-flag areapath)))))))
-;; called in megatest.scm, host-port is string hostname:port
-;;
-;; NOTE: This is NOT called directly from clients as not all transports support a client running
-;; in the same process as the server.
-;;
-(define (server:ping host-port-in server-id #!key (do-exit #f))
- (let ((host:port (if (not host-port-in) ;; use read-dotserver to find
- #f ;; (server:check-if-running *toppath*)
- ;; (if (number? host-port-in) ;; we were handed a server-id
- ;; (let ((srec (tasks:get-server-by-id (db:delay-if-busy (tasks:open-db)) host-port-in)))
- ;; ;; (print "srec: " srec " host-port-in: " host-port-in)
- ;; (if srec
- ;; (conc (vector-ref srec 3) ":" (vector-ref srec 4))
- ;; (conc "no such server-id " host-port-in)))
- host-port-in))) ;; )
- (let* ((host-port (if host:port
- (let ((slst (string-split host:port ":")))
- (if (eq? (length slst) 2)
- (list (car slst)(string->number (cadr slst)))
- #f))
- #f)))
-;; (toppath (launch:setup)))
- ;; (print "host-port=" host-port)
- (if (not host-port)
- (begin
- (if host-port-in
- (debug:print 0 *default-log-port* "ERROR: bad host:port"))
- (if do-exit (exit 1))
- #f)
- (let* ((iface (car host-port))
- (port (cadr host-port))
- (server-dat (http-transport:client-connect iface port server-id))
- (login-res (rmt:login-no-auto-client-setup server-dat)))
- (if (and (list? login-res)
- (car login-res))
- (begin
- ;; (print "LOGIN_OK")
- (if do-exit (exit 0))
- #t)
- (begin
- ;; (print "LOGIN_FAILED")
- (if do-exit (exit 1))
- #f)))))))
-
)
Index: tests.scm
==================================================================
--- tests.scm
+++ tests.scm
@@ -15,6 +15,582 @@
;;
;; You should have received a copy of the GNU General Public License
;; along with Megatest. If not, see .
;;
;;======================================================================
+
+;; summarize test in to a file test-summary.html in the test directory
+;;
+(define (tests:summarize-test run-id test-id)
+ (let* ((test-dat (rmt:get-test-info-by-id run-id test-id))
+ (out-dir (db:test-get-rundir test-dat))
+ (out-file (conc out-dir "/test-summary.html")))
+ ;; first verify we are able to write the output file
+ (if (not (file-writable? out-dir))
+ (debug:print 0 *default-log-port* "ERROR: cannot write test-summary.html to " out-dir)
+ (let* (;; (steps-dat (rmt:get-steps-for-test run-id test-id))
+ (test-name (db:test-get-testname test-dat))
+ (item-path (db:test-get-item-path test-dat))
+ (full-name (db:test-make-full-name test-name item-path))
+ (oup (open-output-file out-file))
+ (status (db:test-get-status test-dat))
+ (color (common:get-color-from-status status))
+ (logf (db:test-get-final_logf test-dat))
+ (steps-dat (tests:get-compressed-steps run-id test-id)))
+ ;; (dcommon:get-compressed-steps #f 1 30045)
+ ;; (#("wasting_time" "23:36:13" "23:36:21" "0" "8.0s" "wasting_time.log"))
+
+ (s:output-new
+ oup
+ (s:html
+ (s:title "Summary for " full-name)
+ (s:body
+ (s:h2 "Summary for " full-name)
+ (s:table 'cellspacing "0" 'border "1"
+ (s:tr (s:td "run id") (s:td (db:test-get-run_id test-dat))
+ (s:td "test id") (s:td (db:test-get-id test-dat)))
+ (s:tr (s:td "testname") (s:td test-name)
+ (s:td "itempath") (s:td item-path))
+ (s:tr (s:td "state") (s:td (db:test-get-state test-dat))
+ (s:td "status") (s:td (s:a 'href logf (s:font 'color color status))))
+ (s:tr (s:td "TestDate") (s:td (seconds->work-week/day-time
+ (db:test-get-event_time test-dat)))
+ (s:td "Duration") (s:td (seconds->hr-min-sec (db:test-get-run_duration test-dat)))))
+ (s:h3 "Log files")
+ (s:table
+ 'cellspacing "0" 'border "1"
+ (s:tr (s:td "Final log")(s:td (s:a 'href logf logf))))
+ (s:table
+ 'cellspacing "0" 'border "1"
+ (s:tr (s:td "Step Name")(s:td "Start")(s:td "End")(s:td "Status")(s:td "Duration")(s:td "Log File"))
+ (map (lambda (step-dat)
+ (s:tr (s:td (tdb:steps-table-get-stepname step-dat))
+ (s:td (tdb:steps-table-get-start step-dat))
+ (s:td (tdb:steps-table-get-end step-dat))
+ (s:td (tdb:steps-table-get-status step-dat))
+ (s:td (tdb:steps-table-get-runtime step-dat))
+ (s:td (let ((step-log (tdb:steps-table-get-log-file step-dat)))
+ (s:a 'href step-log step-log)))))
+ steps-dat))
+ )))
+ (close-output-port oup)))))
+
+
+;; for each test:
+;;
+(define (tests:filter-non-runnable run-id testkeynames testrecordshash)
+ (let ((runnables '()))
+ (for-each
+ (lambda (testkeyname)
+ (let* ((test-record (hash-table-ref testrecordshash testkeyname))
+ (test-name (tests:testqueue-get-testname test-record))
+ (itemdat (tests:testqueue-get-itemdat test-record))
+ (item-path (tests:testqueue-get-item_path test-record))
+ (waitons (tests:testqueue-get-waitons test-record))
+ (keep-test #t)
+ (test-id (rmt:get-test-id run-id test-name item-path))
+ (tdat (rmt:get-testinfo-state-status run-id test-id))) ;; (cdb:get-test-info-by-id *runremote* test-id)))
+ (if tdat
+ (begin
+ ;; Look at the test state and status
+ (if (or (and (member (db:test-get-status tdat)
+ '("PASS" "WARN" "WAIVED" "CHECK" "SKIP"))
+ (equal? (db:test-get-state tdat) "COMPLETED"))
+ (member (db:test-get-state tdat)
+ '("INCOMPLETE" "KILLED")))
+ (set! keep-test #f))
+
+ ;; examine waitons for any fails. If it is FAIL or INCOMPLETE then eliminate this test
+ ;; from the runnable list
+ (if keep-test
+ (for-each (lambda (waiton)
+ ;; for now we are waiting only on the parent test
+ (let* ((parent-test-id (rmt:get-test-id run-id waiton ""))
+ (wtdat (rmt:get-testinfo-state-status run-id test-id))) ;; (cdb:get-test-info-by-id *runremote* test-id)))
+ (if (or (and (equal? (db:test-get-state wtdat) "COMPLETED")
+ (member (db:test-get-status wtdat) '("FAIL" "ABORT")))
+ (member (db:test-get-status wtdat) '("KILLED"))
+ (member (db:test-get-state wtdat) '("INCOMPETE")))
+ ;; (if (or (member (db:test-get-status wtdat)
+ ;; '("FAIL" "KILLED"))
+ ;; (member (db:test-get-state wtdat)
+ ;; '("INCOMPETE")))
+ (set! keep-test #f)))) ;; no point in running this one again
+ waitons))))
+ (if keep-test (set! runnables (cons testkeyname runnables)))))
+ testkeynames)
+ runnables))
+
+;;======================================================================
+;; html output from server
+;;======================================================================
+
+
+(define (tests:dynamic-dboard page)
+;(define (tests:create-html-tree o)
+ (let* (
+;(page "1")
+ (linktree (common:get-linktree))
+ (area-name (common:get-testsuite-name))
+ (keys (rmt:get-keys))
+ (numkeys (length keys))
+ (targtweaked (make-list numkeys "%"))
+ (target-patt (string-join targtweaked "/"))
+ (total-runs (rmt:get-num-runs "%"))
+ (pg-size 10)
+ (pg (if (equal? page #f)
+ 0
+ (- (string->number page) 1)))
+ (get-prev-links (lambda (pg linktree)
+ (debug:print-info 0 *default-log-port* "val: " (- 1 pg))
+ (let* ((link (if (not (eq? pg 0))
+ (s:a "<<prev " 'href (conc "dashboard?page=" pg ))
+ (s:a "" 'href (conc "dashboard?page=" pg)))))
+ link)))
+ (get-next-links (lambda (pg linktree total-runs)
+ (debug:print-info 0 *default-log-port* "val: " pg)
+ (debug:print-info 0 *default-log-port* "val: " total-runs " size" pg-size)
+
+ (let* ((link (if (> total-runs (+ 10 (* pg pg-size)))
+ (s:a "next>> " 'href (conc "dashboard?page=" (+ pg 2) ))
+ (s:a "" 'href (conc "dashboard?page=" pg )))))
+ link)))
+ (html-body (tests:dashboard-body pg pg-size keys numkeys total-runs linktree area-name get-prev-links get-next-links #t "%" target-patt))) ;; update tis function
+ html-body))
+
+(define (tests:create-html-summary outf)
+ (let* ((lockfile (conc outf ".lock"))
+ (linktree (common:get-linktree))
+ (keys (rmt:get-keys))
+ (area-name (common:get-testsuite-name))
+ (run-patt (or (args:get-arg "-run-patt")
+ (args:get-arg "-runname")
+ "%"))
+ (target (or (args:get-arg "-target-patt")
+ (args:get-arg "-target")
+ "%"))
+ (targlist (string-split target "/"))
+ (numkeys (length keys))
+ (numtarg (length targlist))
+ (targtweaked (if (> numkeys numtarg)
+ (append targlist (make-list (- numkeys numtarg) "%"))
+ targlist))
+ (target-patt (string-join targtweaked "/")))
+ (if (common:simple-file-lock lockfile)
+ (begin
+ (let* (;(runsdat1 (rmt:get-runs run-patt #f #f (map (lambda (x)(list x "%")) keys)))
+ (runsdat (rmt:get-runs-by-patt keys run-patt target-patt #f #f #f 0))
+ (runs (vector-ref runsdat 1))
+ (header (vector-ref runsdat 0))
+ (oup (open-output-file (or outf (conc linktree "/targets.html"))))
+ (target-hash (test:create-target-hash runs header (length keys))))
+ (test:create-target-html target-hash oup area-name linktree)
+ (test:create-run-html runs area-name linktree (length keys) header))
+ (common:simple-file-release-lock lockfile))
+ #f)))
+
+(define (test:get-test-hash test-data)
+ (let ((resh (make-hash-table)))
+ (map (lambda (test)
+ (let* ((test-name (vector-ref test 2))
+ (test-html-path (if (file-exists? (conc (vector-ref test 10) "/test-summary.html"))
+ (conc (vector-ref test 10) "/test-summary.html" )
+ (conc (vector-ref test 10) "/" (vector-ref test 13))))
+ (test-item (vector-ref test 11))
+ (test-status (vector-ref test 4)))
+ (if (not (hash-table-ref/default resh test-item #f))
+ (hash-table-set! resh test-item (make-hash-table)))
+ (hash-table-set! (hash-table-ref/default resh test-item #f) test-name (list test-status test-html-path))))
+ test-data)
+resh))
+
+(define (test:get-data->b-keys ordered-data a-keys)
+ (delete-duplicates
+ (sort (apply
+ append
+ (map (lambda (sub-key)
+ (let ((subdat (hash-table-ref ordered-data sub-key)))
+ (hash-table-keys subdat)))
+ a-keys))
+ string>=?)))
+
+
+(define (test:create-run-html runs area-name linktree numkeys header)
+ (map (lambda (run)
+ (let* ((target (string-join (take (vector->list run) numkeys) "/"))
+ (run-name (db:get-value-by-header run header "runname"))
+ (run-time (seconds->work-week/day-time (db:get-value-by-header run header "event_time")))
+ (oup (if (file-exists? (conc linktree "/" target "/" run-name))
+ (open-output-file (conc linktree "/" target "/" run-name "/run.html"))
+ #f))
+ (run-id (db:get-value-by-header run header "id"))
+ (test-data (rmt:get-tests-for-run
+ run-id
+ "%" ;; testnamepatt
+ '() ;; states
+ '() ;; statuses
+ #f ;; offset
+ #f ;; num-to-get
+ #f ;; hide/not-hide
+ #f ;; sort-by
+ #f ;; sort-order
+ #f ;; 'shortlist ;; qrytype
+ 0 ;; last update
+ #f))
+ (item-test-hash (test:get-test-hash test-data))
+ (items (hash-table-keys item-test-hash))
+ (test-names (test:get-data->b-keys item-test-hash items)))
+ (if oup
+ (begin
+ (s:output-new
+ oup
+ (s:html tests:css-jscript-block (tests:css-jscript-block-cond #f)
+ (s:title "Runs View " run-name)
+ (s:body
+ (s:h1 "Runs View " )
+ (s:h3 "Target" target)
+ (s:p
+ (s:b "Run name" ) run-name)
+ (s:p
+ (s:b "Run Date" ) run-time)
+ (s:table 'border 1 'cellspacing 0
+ (s:tr
+ (s:th "Items")
+ (map (lambda (test)
+ (s:th test))
+ test-names))
+ (map (lambda (item)
+ (let* ((test-hash (hash-table-ref/default item-test-hash item #f)))
+ (if test-hash
+ (begin
+ (s:tr
+ (s:td 'class "test" item)
+ (map (lambda (test)
+ (let* ((test-details (hash-table-ref/default test-hash test #f))
+ (status (if test-details
+ (car test-details)))
+ (link (if test-details
+ (string-substitute (conc linktree "/" target "/" run-name "/") "" (cadr test-details) "-"))))
+ (if test-details
+ (s:td 'class status
+ (s:a 'class "link" 'href link status ))
+ (s:td ""))))
+ test-names))))))
+ (sort items string<=?))))))
+ (close-output-port oup))
+ (debug:print-info 0 "Skip: Dirctory structure " linktree "/" target "/" run-name " does not exist. Megatest will not create run.html"))))
+runs))
+
+(define (test:create-target-hash runs header numkeys)
+ (let ((resh (make-hash-table)))
+ (for-each
+ (lambda (run)
+ (let* ((run-name (db:get-value-by-header run header "runname"))
+ (target (string-join (take (vector->list run) numkeys) "/"))
+ (run-list (hash-table-ref/default resh target #f)))
+
+ (if (not run-list)
+ (hash-table-set! resh target (list run-name))
+ (hash-table-set! resh target (cons run-name run-list)))))
+ runs)
+ resh))
+
+(define (test:get-max-run-cnt target-hash targets)
+ (let* ((cnt 0 ))
+ (map (lambda (target)
+ (let* ((runs (hash-table-ref/default target-hash target #f))
+ (run-length (if runs
+ (length runs)
+ 0)))
+
+ (if (< cnt run-length)
+ (set! cnt run-length))))
+ targets)
+cnt))
+
+(define (test:pad-runs target-hash targets max-row-length)
+ (map (lambda (target)
+ (let loop ((run-list (hash-table-ref/default target-hash target #f)))
+ (if (< (length run-list) max-row-length)
+ (begin
+ (hash-table-set! target-hash target (cons "" run-list))
+ (loop (hash-table-ref/default target-hash target #f) )))))
+ targets)
+ target-hash)
+
+(define (test:create-target-html target-hash oup area-name linktree)
+ (let* ((targets (hash-table-keys target-hash))
+ (max-row-length (test:get-max-run-cnt target-hash targets))
+ (pad-runs-hash (test:pad-runs target-hash targets max-row-length)))
+ (s:output-new
+ oup
+ (s:html tests:css-jscript-block (tests:css-jscript-block-cond #f)
+
+ (s:title "Target View " area-name)
+ (s:body
+ (s:h1 "Target View " area-name)
+ (s:table 'id "LinkedList1" 'border "1" 'cellspacing 0
+ (s:tr 'class "something"
+ (s:th "Target")
+ (s:th 'colspan max-row-length "Runs"))
+ (let* ((tbl (map (lambda (target)
+ (s:tr
+ (s:td 'class "test" target)
+ (let* ((runs (hash-table-ref/default target-hash target #f))
+ (rest-row (map (lambda (run)
+ (if (equal? run "")
+ (s:td run)
+ (if (file-exists?(conc linktree "/" target "/" run ))
+ (begin
+ (s:td
+ (s:a 'href (conc target "/" run "/run.html") run))))))
+ (reverse runs))))
+ rest-row)))
+ targets)))
+ tbl)))))
+ (close-output-port oup)))
+
+
+(define (tests:create-html-tree-old outf)
+ (let* ((lockfile (conc outf ".lock"))
+ (runs-to-process '()))
+ (if (common:simple-file-lock lockfile)
+ (let* ((linktree (common:get-linktree))
+ (oup (open-output-file (or outf (conc linktree "/runs-index.html"))))
+ (area-name (common:get-testsuite-name))
+ (keys (rmt:get-keys))
+ (numkeys (length keys))
+ (runsdat (rmt:get-runs "%" #f #f (map (lambda (x)(list x "%")) keys)))
+ (header (vector-ref runsdat 0))
+ (runs (vector-ref runsdat 1))
+ (runtreedat (map (lambda (x)
+ (tests:run-record->test-path x numkeys))
+ runs))
+ (runs-htree (common:list->htree runtreedat)))
+ (set! runs-to-process runs)
+ (s:output-new
+ oup
+ (s:html tests:css-jscript-block
+ (s:title "Summary for " area-name)
+ (s:body 'onload "addEvents();"
+ (s:h1 "Summary for " area-name)
+ ;; top list
+ (s:ul 'id "LinkedList1" 'class "LinkedList"
+ (s:li
+ "Runs"
+ (common:htree->html runs-htree
+ '()
+ (lambda (x p)
+ (let* ((targ-path (string-intersperse p "/"))
+ (full-path (conc linktree "/" targ-path))
+ (run-name (car (reverse p))))
+ (if (and (common:file-exists? full-path)
+ (directory? full-path)
+ (file-writable? full-path))
+ (s:a run-name 'href (conc targ-path "/run-summary.html"))
+ (begin
+ (debug:print 0 *default-log-port* "INFO: Can't create " targ-path "/run-summary.html")
+ (conc run-name " (Not able to create summary at " targ-path ")")))))))))))
+ (close-output-port oup)
+ (common:simple-file-release-lock lockfile)
+
+ (for-each
+ (lambda (run)
+ (let* ((test-subpath (tests:run-record->test-path run numkeys))
+ (run-id (db:get-value-by-header run header "id"))
+ (run-dir (tests:run-record->test-path run numkeys))
+ (test-dats (rmt:get-tests-for-run
+ run-id
+ "%/" ;; testnamepatt
+ '() ;; states
+ '() ;; statuses
+ #f ;; offset
+ #f ;; num-to-get
+ #f ;; hide/not-hide
+ #f ;; sort-by
+ #f ;; sort-order
+ #f ;; 'shortlist ;; qrytype
+ 0 ;; last update
+ #f))
+ (tests-tree-dat (map (lambda (test-dat)
+ ;; (tests:run-record->test-path x numkeys))
+ (let* ((test-name (db:test-get-testname test-dat))
+ (item-path (db:test-get-item-path test-dat))
+ (full-name (db:test-make-full-name test-name item-path))
+ (path-parts (string-split full-name)))
+ path-parts))
+ test-dats))
+ (tests-htree (common:list->htree tests-tree-dat))
+ (html-dir (conc linktree "/" (string-intersperse run-dir "/")))
+ (html-path (conc html-dir "/run-summary.html"))
+ (oup (if (and (common:file-exists? html-dir)
+ (directory? html-dir)
+ (file-writable? html-dir))
+ (open-output-file html-path)
+ #f)))
+ ;; (print "run-dir: " run-dir ", tests-tree-dat: " tests-tree-dat)
+ (if oup
+ (begin
+ (s:output-new
+ oup
+ (s:html tests:css-jscript-block
+ (s:title "Summary for " area-name)
+ (s:body 'onload "addEvents();"
+ (s:h1 "Summary for " (string-intersperse run-dir "/"))
+ ;; top list
+ (s:ul 'id "LinkedList1" 'class "LinkedList"
+ (s:li
+ "Tests"
+ (common:htree->html tests-htree
+ '()
+ (lambda (x p)
+ (let* ((targ-path (string-intersperse p "/"))
+ (test-name (car p))
+ (item-path ;; (if (> (length p) 2) ;; test-name + run-name
+ (string-intersperse p "/"))
+ (full-targ (conc html-dir "/" targ-path))
+ (std-file (conc full-targ "/test-summary.html"))
+ (alt-file (conc full-targ "/megatest-rollup-" test-name ".html"))
+ (html-file (if (common:file-exists? alt-file)
+ alt-file
+ std-file))
+ (run-name (car (reverse p))))
+ (if (and (not (common:file-exists? full-targ))
+ (directory? full-targ)
+ (file-writable? full-targ))
+ (tests:summarize-test
+ run-id
+ (rmt:get-test-id run-id test-name item-path)))
+ (if (common:file-exists? full-targ)
+ (s:a run-name 'href html-file)
+ (begin
+ (debug:print 0 *default-log-port* "ERROR: can't access " full-targ)
+ (conc "No summary for " run-name)))))
+ ))))))
+ (close-output-port oup)))))
+ runs)
+ #t)
+ #f)))
+
+;;======================================================================
+;; web pages
+;;======================================================================
+
+(define (http-transport:html-test-log $)
+ (let* ((run-id ($ 'runid))
+ (test-item ($ 'testname))
+ (parts (string-split test-item ":"))
+ (test-name (car parts))
+
+ (item-name (if (equal? (length parts) 1)
+ ""
+ (cadr parts))))
+ ;(print $)
+ (tests:get-test-log run-id test-name item-name)))
+
+
+(define (http-transport:html-dboard $)
+ (let* ((page ($ 'page))
+ (oup (open-output-string))
+ (bdy "--------------------------")
+
+ (ret (tests:dynamic-dboard page)))
+ (s:output-new oup ret)
+ (close-output-port oup)
+
+ (set! bdy (get-output-string oup))
+ (conc "Dashboard
" bdy "
" )))
+
+(define (http-transport:main-page)
+ (let ((linkpath (root-path)))
+ (conc "" (pathname-strip-directory *toppath*) "
"
+ ""
+ "Run area: " *toppath*
+ "Server Stats
"
+ (http-transport:stats-table)
+ "
"
+ (http-transport:runs linkpath)
+ "
"
+ ;; (http-transport:run-stats)
+ ""
+ )))
+
+(define (http-transport:stats-table)
+ (mutex-lock! *heartbeat-mutex*)
+ (let ((res
+ (conc ""
+ ;; "Max cached queries | " *max-cache-size* " |
"
+ "Number of cached writes | " *number-of-writes* " |
"
+ "Average cached write time | " (if (eq? *number-of-writes* 0)
+ "n/a (no writes)"
+ (/ *writes-total-delay*
+ *number-of-writes*))
+ " ms |
"
+ "Number non-cached queries | " *number-non-write-queries* " |
"
+ ;; "Average non-cached time | " (if (eq? *number-non-write-queries* 0)
+ ;; "n/a (no queries)"
+ ;; (/ *total-non-write-delay*
+ ;; *number-non-write-queries*))
+ " ms |
"
+ "Last access | " (seconds->time-string *db-last-access*) " |
"
+ "
")))
+ (mutex-unlock! *heartbeat-mutex*)
+ res))
+
+(define (http-transport:runs linkpath)
+ (conc "Runs
"
+ (string-intersperse
+ (let ((files (map pathname-strip-directory (glob (conc linkpath "/*")))))
+ (map (lambda (p)
+ (conc "" p "
"))
+ files))
+ " ")))
+
+#;(define (http-transport:run-stats)
+ (let ((stats (open-run-close db:get-running-stats #f)))
+ (conc ""
+ (string-intersperse
+ (map (lambda (stat)
+ (conc "" (car stat) " | " (cadr stat) " |
"))
+ stats)
+ " ")
+ "
")))
+
+(define (tests:get-test-log run-id test-name item-name)
+ (let* ((test-data (rmt:get-tests-for-run
+ (string->number run-id)
+ test-name ;; testnamepatt
+ '() ;; states
+ '() ;; statuses
+ #f ;; offset
+ #f ;; num-to-get
+ #f ;; hide/not-hide
+ #f ;; sort-by
+ #f ;; sort-order
+ #f ;; 'shortlist ;; qrytype
+ 0 ;; last update
+ #f))
+ (path "")
+ (found 0))
+ (debug:print-info 0 *default-log-port* "found: " found )
+
+ (let loop ((hed (car test-data))
+ (tal (cdr test-data)))
+ (debug:print-info 0 *default-log-port* "item: " (vector-ref hed 11) (vector-ref hed 10) "/" (vector-ref hed 13))
+
+ (if (equal? (vector-ref hed 11) item-name)
+ (begin
+ (set! found 1)
+ (set! path (conc (vector-ref hed 10) "/" (vector-ref hed 13)))))
+ (if (and (not (null? tal)) (equal? found 0))
+ (loop (car tal)(cdr tal))))
+ (if (equal? path "")
+ "Data not found
"
+ (string-join (tests:readlines path) "\n"))))
+
+;;===============================================
+;; Java script
+;;===============================================
+(define (http-transport:show-jquery)
+ (let* ((data (tests:readlines *java-script-lib*)))
+(string-join data "\n")))
Index: testsmod.scm
==================================================================
--- testsmod.scm
+++ testsmod.scm
@@ -20,10 +20,12 @@
(declare (unit testsmod))
(declare (uses mtargs))
(declare (uses debugprint))
(declare (uses commonmod))
+(declare (uses itemsmod))
+(declare (uses rmtmod))
(module testsmod
*
(import scheme
@@ -39,15 +41,10 @@
chicken.process-context
chicken.sort
chicken.string
chicken.time
- debugprint
- mtargs
- commonmod
- pkts
-
(prefix base64 base64:)
(prefix dbi dbi:)
(prefix sqlite3 sqlite3:)
(srfi 18)
directory-utils
@@ -63,10 +60,17 @@
srfi-13
srfi-69
stack
typed-records
z3
+
+ debugprint
+ mtargs
+ commonmod
+ pkts
+ itemsmod
+ rmtmod
)
;;======================================================================
;; Tests
@@ -809,404 +813,10 @@
(loop (+ 1 page)))))
(common:simple-file-release-lock lockfile))
(begin
(debug:print 0 *default-log-port* "Failed to get lock on file outf, lockfile: " lockfile) #f))))
-
-(define (tests:readlines filename)
- (call-with-input-file filename
- (lambda (p)
- (let loop ((line (read-line p))
- (result '()))
- (if (eof-object? line)
- (reverse result)
- (loop (read-line p) (cons line result)))))))
-
-(define (tests:get-test-log run-id test-name item-name)
- (let* ((test-data (rmt:get-tests-for-run
- (string->number run-id)
- test-name ;; testnamepatt
- '() ;; states
- '() ;; statuses
- #f ;; offset
- #f ;; num-to-get
- #f ;; hide/not-hide
- #f ;; sort-by
- #f ;; sort-order
- #f ;; 'shortlist ;; qrytype
- 0 ;; last update
- #f))
- (path "")
- (found 0))
- (debug:print-info 0 *default-log-port* "found: " found )
-
- (let loop ((hed (car test-data))
- (tal (cdr test-data)))
- (debug:print-info 0 *default-log-port* "item: " (vector-ref hed 11) (vector-ref hed 10) "/" (vector-ref hed 13))
-
- (if (equal? (vector-ref hed 11) item-name)
- (begin
- (set! found 1)
- (set! path (conc (vector-ref hed 10) "/" (vector-ref hed 13)))))
- (if (and (not (null? tal)) (equal? found 0))
- (loop (car tal)(cdr tal))))
- (if (equal? path "")
- "Data not found
"
- (string-join (tests:readlines path) "\n"))))
-
-
-(define (tests:dynamic-dboard page)
-;(define (tests:create-html-tree o)
- (let* (
-;(page "1")
- (linktree (common:get-linktree))
- (area-name (common:get-testsuite-name))
- (keys (rmt:get-keys))
- (numkeys (length keys))
- (targtweaked (make-list numkeys "%"))
- (target-patt (string-join targtweaked "/"))
- (total-runs (rmt:get-num-runs "%"))
- (pg-size 10)
- (pg (if (equal? page #f)
- 0
- (- (string->number page) 1)))
- (get-prev-links (lambda (pg linktree)
- (debug:print-info 0 *default-log-port* "val: " (- 1 pg))
- (let* ((link (if (not (eq? pg 0))
- (s:a "<<prev " 'href (conc "dashboard?page=" pg ))
- (s:a "" 'href (conc "dashboard?page=" pg)))))
- link)))
- (get-next-links (lambda (pg linktree total-runs)
- (debug:print-info 0 *default-log-port* "val: " pg)
- (debug:print-info 0 *default-log-port* "val: " total-runs " size" pg-size)
-
- (let* ((link (if (> total-runs (+ 10 (* pg pg-size)))
- (s:a "next>> " 'href (conc "dashboard?page=" (+ pg 2) ))
- (s:a "" 'href (conc "dashboard?page=" pg )))))
- link)))
- (html-body (tests:dashboard-body pg pg-size keys numkeys total-runs linktree area-name get-prev-links get-next-links #t "%" target-patt))) ;; update tis function
- html-body))
-
-(define (tests:create-html-summary outf)
- (let* ((lockfile (conc outf ".lock"))
- (linktree (common:get-linktree))
- (keys (rmt:get-keys))
- (area-name (common:get-testsuite-name))
- (run-patt (or (args:get-arg "-run-patt")
- (args:get-arg "-runname")
- "%"))
- (target (or (args:get-arg "-target-patt")
- (args:get-arg "-target")
- "%"))
- (targlist (string-split target "/"))
- (numkeys (length keys))
- (numtarg (length targlist))
- (targtweaked (if (> numkeys numtarg)
- (append targlist (make-list (- numkeys numtarg) "%"))
- targlist))
- (target-patt (string-join targtweaked "/")))
- (if (common:simple-file-lock lockfile)
- (begin
- (let* (;(runsdat1 (rmt:get-runs run-patt #f #f (map (lambda (x)(list x "%")) keys)))
- (runsdat (rmt:get-runs-by-patt keys run-patt target-patt #f #f #f 0))
- (runs (vector-ref runsdat 1))
- (header (vector-ref runsdat 0))
- (oup (open-output-file (or outf (conc linktree "/targets.html"))))
- (target-hash (test:create-target-hash runs header (length keys))))
- (test:create-target-html target-hash oup area-name linktree)
- (test:create-run-html runs area-name linktree (length keys) header))
- (common:simple-file-release-lock lockfile))
- #f)))
-
-(define (test:get-test-hash test-data)
- (let ((resh (make-hash-table)))
- (map (lambda (test)
- (let* ((test-name (vector-ref test 2))
- (test-html-path (if (file-exists? (conc (vector-ref test 10) "/test-summary.html"))
- (conc (vector-ref test 10) "/test-summary.html" )
- (conc (vector-ref test 10) "/" (vector-ref test 13))))
- (test-item (vector-ref test 11))
- (test-status (vector-ref test 4)))
- (if (not (hash-table-ref/default resh test-item #f))
- (hash-table-set! resh test-item (make-hash-table)))
- (hash-table-set! (hash-table-ref/default resh test-item #f) test-name (list test-status test-html-path))))
- test-data)
-resh))
-
-(define (test:get-data->b-keys ordered-data a-keys)
- (delete-duplicates
- (sort (apply
- append
- (map (lambda (sub-key)
- (let ((subdat (hash-table-ref ordered-data sub-key)))
- (hash-table-keys subdat)))
- a-keys))
- string>=?)))
-
-
-(define (test:create-run-html runs area-name linktree numkeys header)
- (map (lambda (run)
- (let* ((target (string-join (take (vector->list run) numkeys) "/"))
- (run-name (db:get-value-by-header run header "runname"))
- (run-time (seconds->work-week/day-time (db:get-value-by-header run header "event_time")))
- (oup (if (file-exists? (conc linktree "/" target "/" run-name))
- (open-output-file (conc linktree "/" target "/" run-name "/run.html"))
- #f))
- (run-id (db:get-value-by-header run header "id"))
- (test-data (rmt:get-tests-for-run
- run-id
- "%" ;; testnamepatt
- '() ;; states
- '() ;; statuses
- #f ;; offset
- #f ;; num-to-get
- #f ;; hide/not-hide
- #f ;; sort-by
- #f ;; sort-order
- #f ;; 'shortlist ;; qrytype
- 0 ;; last update
- #f))
- (item-test-hash (test:get-test-hash test-data))
- (items (hash-table-keys item-test-hash))
- (test-names (test:get-data->b-keys item-test-hash items)))
- (if oup
- (begin
- (s:output-new
- oup
- (s:html tests:css-jscript-block (tests:css-jscript-block-cond #f)
- (s:title "Runs View " run-name)
- (s:body
- (s:h1 "Runs View " )
- (s:h3 "Target" target)
- (s:p
- (s:b "Run name" ) run-name)
- (s:p
- (s:b "Run Date" ) run-time)
- (s:table 'border 1 'cellspacing 0
- (s:tr
- (s:th "Items")
- (map (lambda (test)
- (s:th test))
- test-names))
- (map (lambda (item)
- (let* ((test-hash (hash-table-ref/default item-test-hash item #f)))
- (if test-hash
- (begin
- (s:tr
- (s:td 'class "test" item)
- (map (lambda (test)
- (let* ((test-details (hash-table-ref/default test-hash test #f))
- (status (if test-details
- (car test-details)))
- (link (if test-details
- (string-substitute (conc linktree "/" target "/" run-name "/") "" (cadr test-details) "-"))))
- (if test-details
- (s:td 'class status
- (s:a 'class "link" 'href link status ))
- (s:td ""))))
- test-names))))))
- (sort items string<=?))))))
- (close-output-port oup))
- (debug:print-info 0 "Skip: Dirctory structure " linktree "/" target "/" run-name " does not exist. Megatest will not create run.html"))))
-runs))
-
-(define (test:create-target-hash runs header numkeys)
- (let ((resh (make-hash-table)))
- (for-each
- (lambda (run)
- (let* ((run-name (db:get-value-by-header run header "runname"))
- (target (string-join (take (vector->list run) numkeys) "/"))
- (run-list (hash-table-ref/default resh target #f)))
-
- (if (not run-list)
- (hash-table-set! resh target (list run-name))
- (hash-table-set! resh target (cons run-name run-list)))))
- runs)
- resh))
-
-(define (test:get-max-run-cnt target-hash targets)
- (let* ((cnt 0 ))
- (map (lambda (target)
- (let* ((runs (hash-table-ref/default target-hash target #f))
- (run-length (if runs
- (length runs)
- 0)))
-
- (if (< cnt run-length)
- (set! cnt run-length))))
- targets)
-cnt))
-
-(define (test:pad-runs target-hash targets max-row-length)
- (map (lambda (target)
- (let loop ((run-list (hash-table-ref/default target-hash target #f)))
- (if (< (length run-list) max-row-length)
- (begin
- (hash-table-set! target-hash target (cons "" run-list))
- (loop (hash-table-ref/default target-hash target #f) )))))
- targets)
- target-hash)
-
-(define (test:create-target-html target-hash oup area-name linktree)
- (let* ((targets (hash-table-keys target-hash))
- (max-row-length (test:get-max-run-cnt target-hash targets))
- (pad-runs-hash (test:pad-runs target-hash targets max-row-length)))
- (s:output-new
- oup
- (s:html tests:css-jscript-block (tests:css-jscript-block-cond #f)
-
- (s:title "Target View " area-name)
- (s:body
- (s:h1 "Target View " area-name)
- (s:table 'id "LinkedList1" 'border "1" 'cellspacing 0
- (s:tr 'class "something"
- (s:th "Target")
- (s:th 'colspan max-row-length "Runs"))
- (let* ((tbl (map (lambda (target)
- (s:tr
- (s:td 'class "test" target)
- (let* ((runs (hash-table-ref/default target-hash target #f))
- (rest-row (map (lambda (run)
- (if (equal? run "")
- (s:td run)
- (if (file-exists?(conc linktree "/" target "/" run ))
- (begin
- (s:td
- (s:a 'href (conc target "/" run "/run.html") run))))))
- (reverse runs))))
- rest-row)))
- targets)))
- tbl)))))
- (close-output-port oup)))
-
-
-(define (tests:create-html-tree-old outf)
- (let* ((lockfile (conc outf ".lock"))
- (runs-to-process '()))
- (if (common:simple-file-lock lockfile)
- (let* ((linktree (common:get-linktree))
- (oup (open-output-file (or outf (conc linktree "/runs-index.html"))))
- (area-name (common:get-testsuite-name))
- (keys (rmt:get-keys))
- (numkeys (length keys))
- (runsdat (rmt:get-runs "%" #f #f (map (lambda (x)(list x "%")) keys)))
- (header (vector-ref runsdat 0))
- (runs (vector-ref runsdat 1))
- (runtreedat (map (lambda (x)
- (tests:run-record->test-path x numkeys))
- runs))
- (runs-htree (common:list->htree runtreedat)))
- (set! runs-to-process runs)
- (s:output-new
- oup
- (s:html tests:css-jscript-block
- (s:title "Summary for " area-name)
- (s:body 'onload "addEvents();"
- (s:h1 "Summary for " area-name)
- ;; top list
- (s:ul 'id "LinkedList1" 'class "LinkedList"
- (s:li
- "Runs"
- (common:htree->html runs-htree
- '()
- (lambda (x p)
- (let* ((targ-path (string-intersperse p "/"))
- (full-path (conc linktree "/" targ-path))
- (run-name (car (reverse p))))
- (if (and (common:file-exists? full-path)
- (directory? full-path)
- (file-writable? full-path))
- (s:a run-name 'href (conc targ-path "/run-summary.html"))
- (begin
- (debug:print 0 *default-log-port* "INFO: Can't create " targ-path "/run-summary.html")
- (conc run-name " (Not able to create summary at " targ-path ")")))))))))))
- (close-output-port oup)
- (common:simple-file-release-lock lockfile)
-
- (for-each
- (lambda (run)
- (let* ((test-subpath (tests:run-record->test-path run numkeys))
- (run-id (db:get-value-by-header run header "id"))
- (run-dir (tests:run-record->test-path run numkeys))
- (test-dats (rmt:get-tests-for-run
- run-id
- "%/" ;; testnamepatt
- '() ;; states
- '() ;; statuses
- #f ;; offset
- #f ;; num-to-get
- #f ;; hide/not-hide
- #f ;; sort-by
- #f ;; sort-order
- #f ;; 'shortlist ;; qrytype
- 0 ;; last update
- #f))
- (tests-tree-dat (map (lambda (test-dat)
- ;; (tests:run-record->test-path x numkeys))
- (let* ((test-name (db:test-get-testname test-dat))
- (item-path (db:test-get-item-path test-dat))
- (full-name (db:test-make-full-name test-name item-path))
- (path-parts (string-split full-name)))
- path-parts))
- test-dats))
- (tests-htree (common:list->htree tests-tree-dat))
- (html-dir (conc linktree "/" (string-intersperse run-dir "/")))
- (html-path (conc html-dir "/run-summary.html"))
- (oup (if (and (common:file-exists? html-dir)
- (directory? html-dir)
- (file-writable? html-dir))
- (open-output-file html-path)
- #f)))
- ;; (print "run-dir: " run-dir ", tests-tree-dat: " tests-tree-dat)
- (if oup
- (begin
- (s:output-new
- oup
- (s:html tests:css-jscript-block
- (s:title "Summary for " area-name)
- (s:body 'onload "addEvents();"
- (s:h1 "Summary for " (string-intersperse run-dir "/"))
- ;; top list
- (s:ul 'id "LinkedList1" 'class "LinkedList"
- (s:li
- "Tests"
- (common:htree->html tests-htree
- '()
- (lambda (x p)
- (let* ((targ-path (string-intersperse p "/"))
- (test-name (car p))
- (item-path ;; (if (> (length p) 2) ;; test-name + run-name
- (string-intersperse p "/"))
- (full-targ (conc html-dir "/" targ-path))
- (std-file (conc full-targ "/test-summary.html"))
- (alt-file (conc full-targ "/megatest-rollup-" test-name ".html"))
- (html-file (if (common:file-exists? alt-file)
- alt-file
- std-file))
- (run-name (car (reverse p))))
- (if (and (not (common:file-exists? full-targ))
- (directory? full-targ)
- (file-writable? full-targ))
- (tests:summarize-test
- run-id
- (rmt:get-test-id run-id test-name item-path)))
- (if (common:file-exists? full-targ)
- (s:a run-name 'href html-file)
- (begin
- (debug:print 0 *default-log-port* "ERROR: can't access " full-targ)
- (conc "No summary for " run-name)))))
- ))))))
- (close-output-port oup)))))
- runs)
- #t)
- #f)))
-
-
-
-
-
-
;; CHECK - WAS THIS ADDED OR REMOVED? MANUAL MERGE WITH API STUFF!!!
;;
;; get a pretty table to summarize steps
;;
@@ -1327,67 +937,10 @@
(fprintf outp "~S\n" state)
(fprintf outp "~S\n" status)
(close-output-port outp)))))
-;; summarize test in to a file test-summary.html in the test directory
-;;
-(define (tests:summarize-test run-id test-id)
- (let* ((test-dat (rmt:get-test-info-by-id run-id test-id))
- (out-dir (db:test-get-rundir test-dat))
- (out-file (conc out-dir "/test-summary.html")))
- ;; first verify we are able to write the output file
- (if (not (file-writable? out-dir))
- (debug:print 0 *default-log-port* "ERROR: cannot write test-summary.html to " out-dir)
- (let* (;; (steps-dat (rmt:get-steps-for-test run-id test-id))
- (test-name (db:test-get-testname test-dat))
- (item-path (db:test-get-item-path test-dat))
- (full-name (db:test-make-full-name test-name item-path))
- (oup (open-output-file out-file))
- (status (db:test-get-status test-dat))
- (color (common:get-color-from-status status))
- (logf (db:test-get-final_logf test-dat))
- (steps-dat (tests:get-compressed-steps run-id test-id)))
- ;; (dcommon:get-compressed-steps #f 1 30045)
- ;; (#("wasting_time" "23:36:13" "23:36:21" "0" "8.0s" "wasting_time.log"))
-
- (s:output-new
- oup
- (s:html
- (s:title "Summary for " full-name)
- (s:body
- (s:h2 "Summary for " full-name)
- (s:table 'cellspacing "0" 'border "1"
- (s:tr (s:td "run id") (s:td (db:test-get-run_id test-dat))
- (s:td "test id") (s:td (db:test-get-id test-dat)))
- (s:tr (s:td "testname") (s:td test-name)
- (s:td "itempath") (s:td item-path))
- (s:tr (s:td "state") (s:td (db:test-get-state test-dat))
- (s:td "status") (s:td (s:a 'href logf (s:font 'color color status))))
- (s:tr (s:td "TestDate") (s:td (seconds->work-week/day-time
- (db:test-get-event_time test-dat)))
- (s:td "Duration") (s:td (seconds->hr-min-sec (db:test-get-run_duration test-dat)))))
- (s:h3 "Log files")
- (s:table
- 'cellspacing "0" 'border "1"
- (s:tr (s:td "Final log")(s:td (s:a 'href logf logf))))
- (s:table
- 'cellspacing "0" 'border "1"
- (s:tr (s:td "Step Name")(s:td "Start")(s:td "End")(s:td "Status")(s:td "Duration")(s:td "Log File"))
- (map (lambda (step-dat)
- (s:tr (s:td (tdb:steps-table-get-stepname step-dat))
- (s:td (tdb:steps-table-get-start step-dat))
- (s:td (tdb:steps-table-get-end step-dat))
- (s:td (tdb:steps-table-get-status step-dat))
- (s:td (tdb:steps-table-get-runtime step-dat))
- (s:td (let ((step-log (tdb:steps-table-get-log-file step-dat)))
- (s:a 'href step-log step-log)))))
- steps-dat))
- )))
- (close-output-port oup)))))
-
-
;; MUST BE CALLED local!
;;
(define (tests:test-get-paths-matching keynames target fnamepatt #!key (res '()))
;; BUG: Move the values derived from args to parameters and push to megatest.scm
(let* ((testpatt (or (args:get-arg "-testpatt")(args:get-arg "-testpatt") "%"))
@@ -1689,55 +1242,10 @@
(with-input-from-file fname
(lambda ()
(read-lines)))))))
-;; for each test:
-;;
-(define (tests:filter-non-runnable run-id testkeynames testrecordshash)
- (let ((runnables '()))
- (for-each
- (lambda (testkeyname)
- (let* ((test-record (hash-table-ref testrecordshash testkeyname))
- (test-name (tests:testqueue-get-testname test-record))
- (itemdat (tests:testqueue-get-itemdat test-record))
- (item-path (tests:testqueue-get-item_path test-record))
- (waitons (tests:testqueue-get-waitons test-record))
- (keep-test #t)
- (test-id (rmt:get-test-id run-id test-name item-path))
- (tdat (rmt:get-testinfo-state-status run-id test-id))) ;; (cdb:get-test-info-by-id *runremote* test-id)))
- (if tdat
- (begin
- ;; Look at the test state and status
- (if (or (and (member (db:test-get-status tdat)
- '("PASS" "WARN" "WAIVED" "CHECK" "SKIP"))
- (equal? (db:test-get-state tdat) "COMPLETED"))
- (member (db:test-get-state tdat)
- '("INCOMPLETE" "KILLED")))
- (set! keep-test #f))
-
- ;; examine waitons for any fails. If it is FAIL or INCOMPLETE then eliminate this test
- ;; from the runnable list
- (if keep-test
- (for-each (lambda (waiton)
- ;; for now we are waiting only on the parent test
- (let* ((parent-test-id (rmt:get-test-id run-id waiton ""))
- (wtdat (rmt:get-testinfo-state-status run-id test-id))) ;; (cdb:get-test-info-by-id *runremote* test-id)))
- (if (or (and (equal? (db:test-get-state wtdat) "COMPLETED")
- (member (db:test-get-status wtdat) '("FAIL" "ABORT")))
- (member (db:test-get-status wtdat) '("KILLED"))
- (member (db:test-get-state wtdat) '("INCOMPETE")))
- ;; (if (or (member (db:test-get-status wtdat)
- ;; '("FAIL" "KILLED"))
- ;; (member (db:test-get-state wtdat)
- ;; '("INCOMPETE")))
- (set! keep-test #f)))) ;; no point in running this one again
- waitons))))
- (if keep-test (set! runnables (cons testkeyname runnables)))))
- testkeynames)
- runnables))
-
;;======================================================================
;; refactoring this block into tests:get-full-data from line 263 of runs.scm
;;======================================================================
;; hed is the test name
;; test-records is a hash of test-name => test record