oats_agent 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/.rvmrc +52 -0
- data/Gemfile +4 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/agent.sh~ +189 -0
- data/bin/oats_agent +38 -0
- data/bin/occ +29 -0
- data/bin/results_cleanup +6 -0
- data/lib/oats_agent/commandline_options.rb +68 -0
- data/lib/oats_agent/ragent.rb +323 -0
- data/lib/oats_agent/rclient.rb +42 -0
- data/lib/oats_agent/start.rb +65 -0
- data/lib/oats_agent/version.rb +3 -0
- data/lib/oats_agent.rb +183 -0
- data/nbproject/configs/agent_host.properties +0 -0
- data/nbproject/configs/start.properties +1 -0
- data/nbproject/project.properties +7 -0
- data/nbproject/project.xml +14 -0
- data/oats_agent.gemspec +28 -0
- metadata +143 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# This is an RVM Project .rvmrc file, used to automatically load the ruby
|
4
|
+
# development environment upon cd'ing into the directory
|
5
|
+
|
6
|
+
# First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
|
7
|
+
# Only full ruby name is supported here, for short names use:
|
8
|
+
# echo "rvm use 1.8.7" > .rvmrc
|
9
|
+
environment_id="ruby-1.8.7-p358@oats"
|
10
|
+
|
11
|
+
# Uncomment the following lines if you want to verify rvm version per project
|
12
|
+
# rvmrc_rvm_version="1.13.4 (stable)" # 1.10.1 seams as a safe start
|
13
|
+
# eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
|
14
|
+
# echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
|
15
|
+
# return 1
|
16
|
+
# }
|
17
|
+
|
18
|
+
# First we attempt to load the desired environment directly from the environment
|
19
|
+
# file. This is very fast and efficient compared to running through the entire
|
20
|
+
# CLI and selector. If you want feedback on which environment was used then
|
21
|
+
# insert the word 'use' after --create as this triggers verbose mode.
|
22
|
+
if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
|
23
|
+
&& -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
|
24
|
+
then
|
25
|
+
\. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
|
26
|
+
[[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
|
27
|
+
\. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
|
28
|
+
if [[ $- == *i* ]] # check for interactive shells
|
29
|
+
then echo "Using: $(tput setaf 2)$GEM_HOME$(tput sgr0)" # show the user the ruby and gemset they are using in green
|
30
|
+
else echo "Using: $GEM_HOME" # don't use colors in non-interactive shells
|
31
|
+
fi
|
32
|
+
else
|
33
|
+
# If the environment file has not yet been created, use the RVM CLI to select.
|
34
|
+
rvm --create use "$environment_id" || {
|
35
|
+
echo "Failed to create RVM environment '${environment_id}'."
|
36
|
+
return 1
|
37
|
+
}
|
38
|
+
fi
|
39
|
+
|
40
|
+
# # If you use bundler, this might be useful to you:
|
41
|
+
# if [[ -s Gemfile ]] && {
|
42
|
+
# ! builtin command -v bundle >/dev/null ||
|
43
|
+
# builtin command -v bundle | GREP_OPTIONS= \grep $rvm_path/bin/bundle >/dev/null
|
44
|
+
# }
|
45
|
+
# then
|
46
|
+
# printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
|
47
|
+
# gem install bundler
|
48
|
+
# fi
|
49
|
+
# if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
|
50
|
+
# then
|
51
|
+
# bundle install | GREP_OPTIONS= \grep -vE '^Using|Your bundle is complete'
|
52
|
+
# fi
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# OatsAgent
|
2
|
+
|
3
|
+
With this gem OATS can start in agent mode in the background so that it can
|
4
|
+
communicate with OCC.
|
5
|
+
|
6
|
+
For more information on OATS and OCC see the READMEs in
|
7
|
+
https://github.com/latasoy/oats
|
8
|
+
https://github.com/latasoy/occ
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Install this gem on the machines that will have OATS agents.
|
13
|
+
|
14
|
+
$ gem install oats_agent
|
15
|
+
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
Register the OATS agent with OCC:
|
20
|
+
$ oats_agent -n <agent_nickname> -p <agent_port>
|
21
|
+
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/agent.sh~
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
# This script is used to start OATS in agent mode and communicate with OCC.
|
3
|
+
|
4
|
+
[ "$OS" ] || export OS=$(uname)
|
5
|
+
if [ "$OS" == "Windows_NT" ]; then
|
6
|
+
export PATH="$PATH:/cygdrive/c/apps/cygwin/bin"
|
7
|
+
[ "$HOSTNAME" ] || HOSTNAME="$COMPUTERNAME"
|
8
|
+
else
|
9
|
+
[ "$HOSTNAME" ] || HOSTNAME=`hostname`
|
10
|
+
fi
|
11
|
+
export HOSTNAME
|
12
|
+
[ "$OATS_AGENT_NICKNAME" ] || OATS_AGENT_NICKNAME=${HOSTNAME/.*}
|
13
|
+
doc="
|
14
|
+
Starts OATS agent to be used with OCC
|
15
|
+
|
16
|
+
USAGE: agent [-k[ill]] [-n[ickname] OATS_AGENT_NICKNAME] [ -p[ort] OATS_AGENT_PORT ]
|
17
|
+
[ -r[epo] OATS_TESTS_REPOSITORY_VERSION ] [ -u OATS_USER]
|
18
|
+
|
19
|
+
Parameters: (also can be passed in via environment variables
|
20
|
+
-n OATS_AGENT_NICKNAME OCC ID of the agent.
|
21
|
+
-p OATS_AGENT_PORT Agent port to communicate with OCC. Default is port previously
|
22
|
+
used for OATS_AGENT_NICKNAME.
|
23
|
+
-u OATS_USER who started the agent. Used for logging purposes only.
|
24
|
+
-r OATS_TESTS_REPOSITORY_VERSION Required to update the agent.
|
25
|
+
-k kill the agent, or set OATS_KILL_AGENT
|
26
|
+
-d DISPLAY_NUM Number of the VNC Display, for Linux only
|
27
|
+
|
28
|
+
Example:
|
29
|
+
/home/occadmin/oats/bin/agent -p 3011 -n occ_1 -r 403cc620c265db84dbd3fb7d7cce4d57416bff09
|
30
|
+
|
31
|
+
The agent script should be in the path so that ssh from occ can pick it up.
|
32
|
+
Git requires definition of OATS_TESTS_GIT_REPOSITORY environment variable.
|
33
|
+
"
|
34
|
+
input_pars="$@"
|
35
|
+
while [ "$1" ]; do
|
36
|
+
case $1 in
|
37
|
+
-p*) shift; OATS_AGENT_PORT="$1" ;;
|
38
|
+
-n*) shift; OATS_AGENT_NICKNAME="$1" ;;
|
39
|
+
-u*) shift; OATS_USER="$1" ;;
|
40
|
+
-k*) OATS_KILL_AGENT='OATS_KILL_AGENT' ;;
|
41
|
+
-r*) shift; OATS_TESTS_REPOSITORY_VERSION="$1" ;;
|
42
|
+
-d*) shift; DISPLAY_NUM="$1" ;;
|
43
|
+
*) echo "Unrecognized parameter: $1" ;
|
44
|
+
echo "$doc"
|
45
|
+
exit ;;
|
46
|
+
esac
|
47
|
+
shift
|
48
|
+
done
|
49
|
+
|
50
|
+
function agent_echo {
|
51
|
+
echo "$(date +'%m/%d/%H:%M:%S') $*"
|
52
|
+
}
|
53
|
+
function fkill {
|
54
|
+
agent_echo "Killing PID: $each_pid"
|
55
|
+
if [ "$OS" == "Windows_NT" ]; then
|
56
|
+
pskill $1
|
57
|
+
else
|
58
|
+
kill -9 $1
|
59
|
+
fi
|
60
|
+
}
|
61
|
+
|
62
|
+
{
|
63
|
+
agent_echo "Invoking $0 $input_pars"
|
64
|
+
|
65
|
+
# Need these off when called by OCC, otherwise they OCC values are inherited
|
66
|
+
unset RUBYOPT BUNDLE_BIN_PATH BUNDLE_GEMFILE # GEM_HOME GEM_PATH # RAILS_ENV
|
67
|
+
[ "$OATS_TESTS_GIT_REPOSITORY" ] && export OATS_TESTS="$HOME/results_archive/$OATS_AGENT_NICKNAME"/oats_tests
|
68
|
+
oats_bin="$OATS_HOME/bin"
|
69
|
+
# [ "$OS" == "Windows_NT" ] && oats_bin=$(cygpath -w $oats_bin)
|
70
|
+
# ruby_com="$oats_bin/oats -n $OATS_AGENT_NICKNAME -p $OATS_AGENT_PORT"
|
71
|
+
cur_dir=$(dirname $(type -p $0))
|
72
|
+
ruby_com="$cur_dir/internal_oats_agent_internal -n $OATS_AGENT_NICKNAME -p $OATS_AGENT_PORT"
|
73
|
+
if [ -e "$config_agent_file" ]; then
|
74
|
+
line=$(grep "^$OATS_AGENT_NICKNAME" "$config_agent_file")
|
75
|
+
read nickname PREV_OATS_AGENT_PORT pid display<<< $line
|
76
|
+
[ "$OATS_AGENT_PORT" ] || OATS_AGENT_PORT=$PREV_OATS_AGENT_PORT
|
77
|
+
[ "$PID" ] || PID=$pid
|
78
|
+
if [ -z "$DISPLAY_NUM" ]; then
|
79
|
+
if [ "$display" ]; then
|
80
|
+
DISPLAY_NUM=$(echo "$display" | sed -e 's/.*:\(.\).*/\1/')
|
81
|
+
[ "$DISPLAY_NUM" ] || DISPLAY_NUM="$display"
|
82
|
+
else
|
83
|
+
DISPLAY_NUM='0'
|
84
|
+
fi
|
85
|
+
fi
|
86
|
+
fi
|
87
|
+
if [ -z "$OATS_AGENT_PORT" ]; then
|
88
|
+
agent_echo "$0: Must specify a port, exiting..." >&2
|
89
|
+
exit 1
|
90
|
+
fi
|
91
|
+
|
92
|
+
[ "$OATS_AGENT_PORT" == "$PREV_OATS_AGENT_PORT" ] && PREV_OATS_AGENT_PORT=''
|
93
|
+
for OATS_AGENT_PORT_VAR in $PREV_OATS_AGENT_PORT $OATS_AGENT_PORT; do
|
94
|
+
skip_port=''
|
95
|
+
if [ "$PID" -a "$OATS_AGENT_PORT_VAR" == "$PREV_OATS_AGENT_PORT" ]; then
|
96
|
+
agent_echo "Attempting to kill agent $OATS_AGENT_NICKNAME with PID: $PID"
|
97
|
+
fkill $PID
|
98
|
+
[ $? == 0 ] && skip_port=skip_port
|
99
|
+
fi
|
100
|
+
if [ -z "$skip_port" ]; then
|
101
|
+
# agent_echo "Attempting to kill process holding on to port $OATS_AGENT_PORT_VAR"
|
102
|
+
if [ "$OS" == Darwin ]; then
|
103
|
+
lines=$(ps -ef|grep "$ruby_com"|grep -v grep)
|
104
|
+
PID=$(echo $(cut -c 6-11 <<< "$lines"))
|
105
|
+
else
|
106
|
+
if [ "$OS" == Windows_NT ]; then # Killing the top level bash process doesn't cut it
|
107
|
+
lines=$(netstat -a -o 2>/dev/null| grep "$OATS_AGENT_PORT_VAR .* LISTEN" )
|
108
|
+
else
|
109
|
+
lines=$(netstat -n -p -l 2>/dev/null| grep "$OATS_AGENT_PORT_VAR .* LISTEN" )
|
110
|
+
fi
|
111
|
+
PID=$(echo "$line" | sed 's/.* LISTEN[^ ]* *//;s/.ruby .*//')
|
112
|
+
[ "$PID" -a "$PID" != "$line" ] || PID=''
|
113
|
+
fi
|
114
|
+
if [ "$PID" ]; then
|
115
|
+
agent_echo "PS line(s): $lines"
|
116
|
+
for each_pid in $PID; do
|
117
|
+
fkill $each_pid
|
118
|
+
done
|
119
|
+
# else
|
120
|
+
# agent_echo "No agent is found on port $OATS_AGENT_PORT_VAR"
|
121
|
+
fi
|
122
|
+
fi
|
123
|
+
done
|
124
|
+
[ "$OATS_KILL_AGENT" ] && return
|
125
|
+
|
126
|
+
if [ -d $OATS_TESTS/.svn ]; then
|
127
|
+
COUNTER=0
|
128
|
+
[ "$OATS_TESTS_REPOSITORY_VERSION" ] && agent_echo "Requested OATS Version: $OATS_TESTS_REPOSITORY_VERSION"
|
129
|
+
while [ $COUNTER -lt 5 ]; do
|
130
|
+
let COUNTER=COUNTER+1
|
131
|
+
OATS_TESTS_CODE_VERSION=$(svn info $OATS_TESTS | sed -n 's/Last Changed Rev: *//p')
|
132
|
+
[ "$OATS_TESTS_REPOSITORY_VERSION" ] || break
|
133
|
+
[ "$OATS_TESTS_CODE_VERSION" -ge "$OATS_TESTS_REPOSITORY_VERSION" ] && break
|
134
|
+
[ $COUNTER -eq 1 ] || sleep 3
|
135
|
+
svn update $OATS_TESTS 2>&1
|
136
|
+
done
|
137
|
+
if [ $COUNTER -eq 5 ]; then
|
138
|
+
agent_echo "Could not update the code version $OATS_TESTS_CODE_VERSION to $OATS_TESTS_REPOSITORY_VERSION" >&2
|
139
|
+
return 2
|
140
|
+
fi
|
141
|
+
agent_echo "Current OATS code version: $OATS_TESTS_CODE_VERSION"
|
142
|
+
elif [ "$OATS_TESTS_GIT_REPOSITORY" ]; then
|
143
|
+
[ -d $OATS_TESTS ] || git clone $OATS_TESTS_GIT_REPOSITORY $OATS_TESTS
|
144
|
+
cd $OATS_TESTS
|
145
|
+
origin="$OATS_TESTS_GIT_REPOSITORY"
|
146
|
+
[ "$origin" ] || origin=origin
|
147
|
+
if [ "$OATS_TESTS_REPOSITORY_VERSION" ]; then
|
148
|
+
for i in 1 2; do
|
149
|
+
out=$(git checkout $OATS_TESTS_REPOSITORY_VERSION 2>&1) && break # may detach HEAD, but it is OK
|
150
|
+
if [ "$out" == "fatal: reference is not a tree: $OATS_TESTS_REPOSITORY_VERSION" ]; then
|
151
|
+
echo -n "Need to pull requested version: $OATS_TESTS_REPOSITORY_VERSION "
|
152
|
+
else
|
153
|
+
echo "$out"
|
154
|
+
fi
|
155
|
+
git pull $origin master # fast-forward master from origin
|
156
|
+
done
|
157
|
+
else
|
158
|
+
git pull $origin master # fast-forward master from origin
|
159
|
+
fi
|
160
|
+
OATS_TESTS_CODE_VERSION=$(git rev-list HEAD -1) # last commit in checked out version
|
161
|
+
if [ "${OATS_TESTS_CODE_VERSION##$OATS_TESTS_REPOSITORY_VERSION*}" ]; then
|
162
|
+
agent_echo "Could not update the code version $OATS_TESTS_CODE_VERSION to $OATS_TESTS_REPOSITORY_VERSION" >&2
|
163
|
+
return 2
|
164
|
+
fi
|
165
|
+
[ "$OATS_TESTS_REPOSITORY_VERSION" ] || agent_echo "Using OATS code version: $OATS_TESTS_CODE_VERSION"
|
166
|
+
else
|
167
|
+
OATS_TESTS_CODE_VERSION=$OATS_TESTS_REPOSITORY_VERSION
|
168
|
+
[ "$OATS_TESTS_CODE_VERSION" ] && agent_echo "Setting OATS code version to the requested version: $OATS_TESTS_CODE_VERSION]" # This is not set for development/debug
|
169
|
+
fi
|
170
|
+
export OATS_TESTS_CODE_VERSION
|
171
|
+
# echo "$OATS_AGENT_NICKNAME $OATS_AGENT_PORT" >| $config_agent_file
|
172
|
+
if [ "$OS" == "Linux" ]; then # Do this with VNC displays
|
173
|
+
# Allocate 2x as display. VNC takes 592x, vino takes next available from 59++
|
174
|
+
# vnc_num="2$(echo $OATS_AGENT_PORT | cut -c 4)"
|
175
|
+
vnc_num="$DISPLAY_NUM"
|
176
|
+
[ "$vnc_num" ] || vnc_num="1"
|
177
|
+
pgrep -fl "Xvnc4 :$vnc_num" >/dev/null || vncserver :$vnc_num -SecurityTypes=None -geometry 1900x1060
|
178
|
+
export DISPLAY=":$vnc_num.0"
|
179
|
+
agent_echo "Setting DISPLAY to $DISPLAY"
|
180
|
+
fi
|
181
|
+
cd $OATS_HOME # Needed for bundler
|
182
|
+
agent_echo "Starting agent $OATS_AGENT_NICKNAME on port $OATS_AGENT_PORT"
|
183
|
+
$ruby_com
|
184
|
+
} >> $OATS_AGENT_LOGFILE 2>&1 &
|
185
|
+
|
186
|
+
PID=$(jobs -p)
|
187
|
+
echo "Initiated PID: $PID" >> $OATS_AGENT_LOGFILE
|
188
|
+
echo "$PID"
|
189
|
+
echo "$OATS_AGENT_NICKNAME $OATS_AGENT_PORT $PID $DISPLAY_NUM" >| $OATS_AGENT_CONFIG_FILE
|
data/bin/oats_agent
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
# To pickup non-gem oats for local debugging
|
5
|
+
|
6
|
+
if File.directory? agent_dir = File.expand_path('../../../oats_agent/lib', __FILE__)
|
7
|
+
$:.unshift agent_dir
|
8
|
+
gem 'log4r'
|
9
|
+
require 'oats_agent' # bundler fails when it can't find the gem if not in here
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'oats_agent/commandline_options'
|
13
|
+
require 'log4r'
|
14
|
+
|
15
|
+
Log4r::Logger.root.level = Log4r::DEBUG
|
16
|
+
Log4r::StdoutOutputter.new('console', :level=>1,
|
17
|
+
:formatter=>Log4r::PatternFormatter.new(:depth=>50,
|
18
|
+
:pattern => "%-5l %d %M", :date_pattern=>"%y-%m-%d %H:%M:%S"))
|
19
|
+
$log = Log4r::Logger.new('R')
|
20
|
+
$log.add('console')
|
21
|
+
options = OatsAgent::CommandlineOptions.options(ARGV.dup)
|
22
|
+
if options["agent_host"].nil? or options["agent_host"] == ENV['HOSTNAME']
|
23
|
+
OatsAgent.spawn(options)
|
24
|
+
else
|
25
|
+
params = ARGV.dup
|
26
|
+
if RUBY_PLATFORM =~ /(mswin|mingw)/
|
27
|
+
cmd = "psexec.exe -d -i -n 10 -w " + ENV['HOME'] + '/results_archive' +
|
28
|
+
' -u qa -p ' + 'passwd' + ' \\\\' + options["agent_host"] +
|
29
|
+
' ruby oats_agent ' + params.join(' ')
|
30
|
+
else
|
31
|
+
# params.delete('-a')
|
32
|
+
# params.delete(options["agent_host"])
|
33
|
+
# options['agent_host'] = ENV['HOSTNAME']
|
34
|
+
cmd = "ssh " + options["agent_host"] + ' oats_agent ' + params.join(' ')
|
35
|
+
end
|
36
|
+
out = `#{cmd}`
|
37
|
+
$log.info out unless out == ''
|
38
|
+
end
|
data/bin/occ
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This script is used to start OCC
|
4
|
+
|
5
|
+
#[ "$RAILS_ENV" ] || export RAILS_ENV="production"
|
6
|
+
occ_home = ENV['OATS_OCC_HOME']
|
7
|
+
unless occ_home
|
8
|
+
pwd = Dir.pwd
|
9
|
+
occ_home = pwd if File.basename(pwd) == 'occ'
|
10
|
+
end
|
11
|
+
occ_home = ENV['HOME'] + '/occ' unless occ_home
|
12
|
+
occ_home = ENV['HOME'] + '/NetBeansProjects/occ' unless File.directory?(occ_home)
|
13
|
+
|
14
|
+
raise "Must define OCC_HOME or be in occ directory to run this command." unless occ_home
|
15
|
+
|
16
|
+
Dir.chdir(occ_home)
|
17
|
+
exec 'ruby script/rails server mongrel' # -p 4000
|
18
|
+
|
19
|
+
# By pass the following unless using distributed agents
|
20
|
+
# if [ "$OS" != "Windows_NT" ]; then
|
21
|
+
# if [ -z "$SSH_AGENT_PID" ] || ! ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null ; then
|
22
|
+
# echo "Should start_agent for SSH first"
|
23
|
+
# # exit 1
|
24
|
+
# fi
|
25
|
+
# ssh-add -l || echo "*** WARNING *** Please add your ssh keys ssh-add"
|
26
|
+
# fi
|
27
|
+
|
28
|
+
|
29
|
+
#mongrel_rails start -e production -p 3000 --log log\production.log
|
data/bin/results_cleanup
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'optparse' # http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/index.html
|
2
|
+
require 'log4r' # http://log4r.sourceforge.net/rdoc/index.html
|
3
|
+
|
4
|
+
module OatsAgent
|
5
|
+
|
6
|
+
module CommandlineOptions
|
7
|
+
|
8
|
+
@@OPTIONS = nil
|
9
|
+
def CommandlineOptions.options(argv = nil)
|
10
|
+
|
11
|
+
begin
|
12
|
+
|
13
|
+
# Hold all of the options parsed from the command-line by OptionParser.
|
14
|
+
options = {}
|
15
|
+
optparse = OptionParser.new do|opts|
|
16
|
+
opts.banner = "Usage: oats.rb [options] test1 test2 ..."
|
17
|
+
opts.separator "Options:"
|
18
|
+
|
19
|
+
opts.on( '-p', '--port PORT', Integer,
|
20
|
+
'Port number for the Oats Agent.' ) do |t|
|
21
|
+
options['port'] = t if t
|
22
|
+
end
|
23
|
+
opts.on( '-n', '--nickname NICKNAME',
|
24
|
+
'Nickname to display on OCC for the Oats Agent.' ) do |t|
|
25
|
+
options['nickname'] = t if t
|
26
|
+
end
|
27
|
+
opts.on( '-u', '--user USER',
|
28
|
+
'Sets OATS_USER for agent' ) do |t|
|
29
|
+
options['user'] = t if t
|
30
|
+
end
|
31
|
+
opts.on( '-r', '--repository_version REPOSITORY_VERSION',
|
32
|
+
'Repository version requested' ) do |t|
|
33
|
+
options['repository_version'] = t
|
34
|
+
end
|
35
|
+
opts.on( '-a', '--agent_host HOSTNAME',
|
36
|
+
'Hostname where the agent should start.' ) do |t|
|
37
|
+
options['agent_host'] = t if t
|
38
|
+
end
|
39
|
+
opts.on( '-k', '--kill_agent',
|
40
|
+
'Kill the agent.' ) do |t|
|
41
|
+
options['kill_agent'] = true
|
42
|
+
end
|
43
|
+
opts.on( '-t', '--test_directory DIR_TESTS',
|
44
|
+
'Test directory to override environment variable OATS_TESTS.' ) do |t|
|
45
|
+
options['dir_tests'] = t
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
opts.on_tail( '-h', '--help', 'Display this screen' ) { $stderr.puts opts; exit }
|
50
|
+
end
|
51
|
+
|
52
|
+
optparse.parse!(argv)
|
53
|
+
if argv and ! argv.empty?
|
54
|
+
options['execution:test_files'] ||= []
|
55
|
+
options['execution:test_files'] += argv
|
56
|
+
end
|
57
|
+
|
58
|
+
rescue Exception => e
|
59
|
+
raise unless e.class.to_s =~ /^OptionParser::/
|
60
|
+
$stderr.puts e.message
|
61
|
+
$stderr.puts "Please type 'oats_agent -h' for valid options."
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
@@OPTIONS = options
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,323 @@
|
|
1
|
+
#require 'eventmachine' # http://eventmachine.rubyforge.org/EventMachine.html#M000486
|
2
|
+
#require 'patches_for_eventmachine_12.10'
|
3
|
+
require 'em-http-request' # https://github.com/igrigorik/em-http-request/wiki/Issuing-Requests
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module OatsAgent
|
7
|
+
|
8
|
+
class Ragent < EventMachine::Connection
|
9
|
+
attr_accessor :job_count, :jid, :occ_reintroduction_wait_time
|
10
|
+
attr_reader :request
|
11
|
+
@@logger = nil
|
12
|
+
@@oats_info_snapshot = {}
|
13
|
+
@@job_count = 1
|
14
|
+
@@occ_default = nil
|
15
|
+
include EM::P::ObjectProtocol
|
16
|
+
|
17
|
+
|
18
|
+
# Non-nil if in the state of requesting for the next job
|
19
|
+
def Ragent.in_next_job
|
20
|
+
@@in_next_job
|
21
|
+
end
|
22
|
+
def Ragent.in_next_job=(v)
|
23
|
+
@@in_next_job = v
|
24
|
+
end
|
25
|
+
def Ragent.is_busy=(jid)
|
26
|
+
@@is_busy = jid
|
27
|
+
end
|
28
|
+
# Current/Last jobid worked on, or false
|
29
|
+
def Ragent.is_busy
|
30
|
+
@@is_busy
|
31
|
+
end
|
32
|
+
|
33
|
+
# Contains YAML OCC entries if oats is started in agent mode
|
34
|
+
def Ragent.occ
|
35
|
+
@@occ_default
|
36
|
+
end
|
37
|
+
def Ragent.logger
|
38
|
+
@@logger ||= Log4r::Logger.new('A')
|
39
|
+
end
|
40
|
+
|
41
|
+
def Ragent.start(occ_def)
|
42
|
+
@@occ_default = occ_def # This should not change during agent execution
|
43
|
+
3.times do |count| # In case of unexpected exceptions
|
44
|
+
begin
|
45
|
+
@@occ_reintroduction_wait_time = nil
|
46
|
+
@@is_busy = false # If agent is started from scratch assume previous one is gone
|
47
|
+
@@in_next_job = false
|
48
|
+
@@logger.info "====================================================================================="
|
49
|
+
mach_port = ENV['HOSTNAME'] + ':' + $oats['execution']['occ']['port'].to_s
|
50
|
+
@@logger.info "Started OATS Server execution-#{count} on #{mach_port} at #{Time.now} "
|
51
|
+
EventMachine::run do
|
52
|
+
EventMachine::start_server @@occ_default['agent_host'], @@occ_default['agent_port'].to_i, Ragent
|
53
|
+
EventMachine.next_tick do
|
54
|
+
Ragent.server_logger nil,'Initiating contact with OCC '
|
55
|
+
Ragent.start_next_job unless Ragent.is_busy
|
56
|
+
end
|
57
|
+
end
|
58
|
+
break # Shutdown requested
|
59
|
+
rescue Exception => exc
|
60
|
+
@@logger.error exc
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# def Ragent.force_close_connection
|
66
|
+
# end
|
67
|
+
def Ragent.job_count=(val)
|
68
|
+
@@job_count = val
|
69
|
+
end
|
70
|
+
def Ragent.job_count
|
71
|
+
@@job_count
|
72
|
+
end
|
73
|
+
|
74
|
+
def Ragent.snapshot_oats_info(oats_info)
|
75
|
+
@@oats_info_snapshot = oats_info
|
76
|
+
end
|
77
|
+
|
78
|
+
# Generates summary data into the input oats_info
|
79
|
+
def regen_results_summary!(oats_info)
|
80
|
+
return Oats::Report.results(oats_info['test_files'],true)
|
81
|
+
rescue
|
82
|
+
server_logger $!.inspect + "\n" + $!.backtrace.join("\n ")
|
83
|
+
false
|
84
|
+
end
|
85
|
+
|
86
|
+
# Results_status: Early, Partial, Current, Archived, Missing, Error
|
87
|
+
def get_oats_info
|
88
|
+
if @request[:jobid] == Ragent.is_busy # working on it now
|
89
|
+
if @request[:jobid] == @@oats_info_snapshot['jobid'] # first test started processing
|
90
|
+
if regen_results_summary!(@@oats_info_snapshot) # summary succeeded
|
91
|
+
oats_info = Marshal.load(Marshal.dump(@@oats_info_snapshot))
|
92
|
+
oats_info['results_status'] = 'Partial'
|
93
|
+
else
|
94
|
+
server_logger "[ERROR] Can not regen_results_summary from the snapshot"
|
95
|
+
oats_info = {'results_status' => 'Error'}
|
96
|
+
end
|
97
|
+
elsif @@oats_info_snapshot['jobid']
|
98
|
+
msg = "ERROR: Unexpected condition. Request jobid: #{@request[:jobid]} does not match stored job id: #{@@oats_info_snapshot['jobid']}"
|
99
|
+
server_logger msg
|
100
|
+
oats_info = { 'jobid' => @request[:jobid], 'results_status' => 'Error', 'error_message' => msg }
|
101
|
+
else
|
102
|
+
oats_info = Marshal.load(Marshal.dump(Oats.context))
|
103
|
+
oats_info['results_status'] = 'Early'
|
104
|
+
oats_info['jobid'] = Ragent.is_busy
|
105
|
+
end
|
106
|
+
else # Search for request on disk in archive or results
|
107
|
+
res_dir = File.join(Oats.result_archive_dir, @request[:jobid].to_s)
|
108
|
+
results_file = File.join( res_dir,'results.dump')
|
109
|
+
if File.readable?(results_file) # Archived ones should have summary
|
110
|
+
oats_info = Oats::Report.oats_info_retrieve(results_file)
|
111
|
+
oats_info['results_status'] = 'Archived'
|
112
|
+
else # May have to regenerate the summary, in case test had died
|
113
|
+
results_file = File.join( $oats['execution']['dir_results'], 'results.dump')
|
114
|
+
if File.readable?(results_file)
|
115
|
+
oats_info = Oats::Report.oats_info_retrieve(results_file)
|
116
|
+
if regen_results_summary!(oats_info) and @request[:jobid] == oats_info['jobid']
|
117
|
+
oats_info['results_status'] = 'Current'
|
118
|
+
else
|
119
|
+
oats_info = {} unless oats_info.instance_of?(Hash)
|
120
|
+
oats_info['debug_message'] = "request_jobid: #{@request[:jobid]}, current oats_info jobid: #{oats_info['jobid']}"
|
121
|
+
oats_info['results_status'] = 'Missing'
|
122
|
+
end
|
123
|
+
else
|
124
|
+
oats_info = { 'results_status' => 'Missing' ,
|
125
|
+
'debug_message' => "No readable: #{results_file}" }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
# Convert object to hash
|
130
|
+
oats_info['test_files'] = oats_info['test_files'].testlist_hash if oats_info['test_files']
|
131
|
+
return oats_info
|
132
|
+
rescue
|
133
|
+
server_logger $!.inspect + "\n" + $!.backtrace.join("\n ")
|
134
|
+
end
|
135
|
+
|
136
|
+
def receive_object(request)
|
137
|
+
@request = request
|
138
|
+
password = @request.delete(:password)
|
139
|
+
server_logger "Received " + @request.inspect
|
140
|
+
@request[:password] = password
|
141
|
+
response = {}
|
142
|
+
case @request[:command]
|
143
|
+
|
144
|
+
when 'status'
|
145
|
+
EventMachine.next_tick { run_next_job } unless Ragent.is_busy
|
146
|
+
|
147
|
+
when 'start'
|
148
|
+
if Ragent.is_busy
|
149
|
+
server_logger "Not getting next job again because Ragent.is_busy: #{Ragent.is_busy}"
|
150
|
+
else
|
151
|
+
EventMachine.next_tick { run_next_job }
|
152
|
+
end
|
153
|
+
|
154
|
+
when 'results'
|
155
|
+
begin
|
156
|
+
response[:oats_info] = get_oats_info
|
157
|
+
rescue
|
158
|
+
server_logger $!.inspect + "\n" + $!.backtrace.join("\n ")
|
159
|
+
end
|
160
|
+
|
161
|
+
when 'run' # only called from oats client, not from OCC
|
162
|
+
EventMachine.defer( proc {
|
163
|
+
Oats::Driver.start(@request[:jobid], @request[:args])
|
164
|
+
} ) unless Ragent.is_busy
|
165
|
+
|
166
|
+
when 'stop' # any further test execution for this jobid
|
167
|
+
Oats.context['stop_oats'] = @request[:id] if @request[:stop_jobs].include?(Oats.context['jobid'])
|
168
|
+
|
169
|
+
when 'shutdown'
|
170
|
+
else
|
171
|
+
response[:unknown_command] = true
|
172
|
+
server_logger "Unknown command #{@request[:command]}"
|
173
|
+
end
|
174
|
+
response[:is_busy] = Ragent.is_busy
|
175
|
+
stop_oats = Oats.context && Oats.context['stop_oats']
|
176
|
+
response[:is_signal_oats_to_stop] = stop_oats if stop_oats
|
177
|
+
server_logger "Sending " + response.inspect
|
178
|
+
response[:password] = password
|
179
|
+
send_object(response)
|
180
|
+
close_connection_after_writing
|
181
|
+
rescue
|
182
|
+
server_logger $!.inspect + "\n" + $!.backtrace.join("\n ")
|
183
|
+
end
|
184
|
+
|
185
|
+
def unbind
|
186
|
+
if @request[:command] == 'shutdown'
|
187
|
+
server_logger "Shutting down the server."
|
188
|
+
EventMachine::stop_event_loop
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def run_next_job(prev_jobid = nil)
|
193
|
+
return unless @request[:occ_host] # Bad input or invoked via client, not occ
|
194
|
+
occ = @@occ_default.clone
|
195
|
+
occ['server_host'] = @request[:occ_host]
|
196
|
+
occ['server_port'] = @request[:occ_port]
|
197
|
+
Ragent.start_next_job(occ, self, prev_jobid)
|
198
|
+
end
|
199
|
+
|
200
|
+
def self.start_next_job(occ = @@occ_default, ra = nil, prev_jobid = nil)
|
201
|
+
if Ragent.in_next_job or Ragent.is_busy
|
202
|
+
msg = if Ragent.in_next_job
|
203
|
+
if Ragent.in_next_job == nil.object_id
|
204
|
+
"already getting the initial job."
|
205
|
+
else
|
206
|
+
"already getting job for #{Ragent.in_next_job}]"
|
207
|
+
end
|
208
|
+
else
|
209
|
+
"became busy with #{Ragent.is_busy}"
|
210
|
+
end
|
211
|
+
Ragent.server_logger ra, "Not requesting new job since #{msg}"
|
212
|
+
return false
|
213
|
+
end
|
214
|
+
Ragent.in_next_job = ra.object_id
|
215
|
+
# Double check that this agent has the busy lock
|
216
|
+
if not Ragent.in_next_job or Ragent.in_next_job != ra.object_id
|
217
|
+
Ragent.server_logger(ra, "Not requesting new job since " +
|
218
|
+
"now another Ragent #{ra.object_id} is requesting next job besides current #{Ragent.in_next_job}")
|
219
|
+
return false
|
220
|
+
end
|
221
|
+
query = {
|
222
|
+
'nickname' => occ['agent_nickname'],
|
223
|
+
'machine' => occ['agent_host'],
|
224
|
+
'port' => occ['agent_port'] }
|
225
|
+
query['jobid'] = prev_jobid if prev_jobid
|
226
|
+
query['repo'] = ENV['OATS_TESTS_CODE_VERSION'].to_s if ENV['OATS_TESTS_CODE_VERSION'] and ENV['OATS_TESTS_CODE_VERSION'] != ''
|
227
|
+
query['logfile'] = File.basename(ENV['OATS_AGENT_LOGFILE']||'agent.log')
|
228
|
+
Ragent.server_logger ra, "Getting next OCC job: " + query.inspect
|
229
|
+
query['password='] = ra.request[:password] if ra and ra.request[:password]
|
230
|
+
# Default inactivity_timeout of 10 is not enough when OCC is restarting too
|
231
|
+
# many agents. Agent gives up in 10secs but OCC hands over the job in 20secs.
|
232
|
+
# As a result OCC thinks job is received but agent has never seen the job.
|
233
|
+
connection_options = { :connect_timeout => 60,:inactivity_timeout => 60}
|
234
|
+
http_req = EventMachine::HttpRequest.new('http://' + occ['server_host'] + ":#{occ['server_port']}",connection_options)
|
235
|
+
http = http_req.get :path => '/jobs/nxt', :query => query
|
236
|
+
http.errback { self.no_response(occ,ra, prev_jobid) }
|
237
|
+
http.callback do
|
238
|
+
status = http.response_header.status
|
239
|
+
if status == 200
|
240
|
+
data =http.response
|
241
|
+
nxt_job = JSON.parse(data) if data
|
242
|
+
if nxt_job['jid']
|
243
|
+
Ragent.is_busy = nxt_job['jid']
|
244
|
+
Ragent.in_next_job = false
|
245
|
+
if ra
|
246
|
+
ra.jid = nxt_job['jid']
|
247
|
+
ra.occ_reintroduction_wait_time = nil # Reset wait time to default if heard from OCC
|
248
|
+
end
|
249
|
+
Ragent.server_logger ra, "Job-#{Ragent.job_count} #{nxt_job.inspect}"
|
250
|
+
EventMachine.defer do
|
251
|
+
begin
|
252
|
+
opts= {'execution:environments' => [nxt_job['env']],
|
253
|
+
'execution:test_files' => [nxt_job['list']] }
|
254
|
+
opts['_:options'] = nxt_job['options'].split(',') if nxt_job['options'] and nxt_job['options'] != ''
|
255
|
+
Ragent.snapshot_oats_info({})
|
256
|
+
Oats::Driver.start(nxt_job['jid'],opts)
|
257
|
+
ensure
|
258
|
+
Ragent.is_busy = false
|
259
|
+
end
|
260
|
+
Ragent.job_count += 1
|
261
|
+
if ra
|
262
|
+
ra.run_next_job(nxt_job['jid'] )
|
263
|
+
else
|
264
|
+
Ragent.start_next_job(occ,ra,nxt_job['jid'])
|
265
|
+
end
|
266
|
+
end
|
267
|
+
else
|
268
|
+
Ragent.in_next_job = false
|
269
|
+
Ragent.server_logger ra, "No more pending jobs at OCC. Pausing processing.\n"
|
270
|
+
Ragent.job_count = 1
|
271
|
+
Ragent.server_logger ra, "***********************************************************\n"
|
272
|
+
end
|
273
|
+
else
|
274
|
+
self.no_response(occ,ra, prev_jobid)
|
275
|
+
end
|
276
|
+
end
|
277
|
+
rescue RuntimeError => e.message
|
278
|
+
Ragent.in_next_job = false
|
279
|
+
if e.message == 'eventmachine not initialized: evma_connect_to_server'
|
280
|
+
Ragent.server_logger ra, "Shutting down..."
|
281
|
+
else
|
282
|
+
Ragent.server_logger ra, $!.inspect + "\n" + $!.backtrace.join("\n ")
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def self.no_response(occv,ra, prev_jobid)
|
287
|
+
Ragent.in_next_job = false
|
288
|
+
Ragent.server_logger ra, "OCC did not respond."
|
289
|
+
wait = ra ? ra.occ_reintroduction_wait_time : @@occ_reintroduction_wait_time
|
290
|
+
wait ||= $oats['execution']['occ']['timeout_waiting_for_occ']
|
291
|
+
# Keep retrying to introduce, doubling the intervals
|
292
|
+
self.server_logger ra, "Will retry in #{wait} seconds."
|
293
|
+
EM.add_timer(wait) do
|
294
|
+
Ragent.start_next_job(occv,ra, prev_jobid) unless Ragent.is_busy # by now
|
295
|
+
end
|
296
|
+
wait *= (1.5 + rand(101)/100.0)
|
297
|
+
wait = wait.round
|
298
|
+
if ra
|
299
|
+
ra.occ_reintroduction_wait_time = wait
|
300
|
+
else
|
301
|
+
@@occ_reintroduction_wait_time = wait
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def server_logger(arg)
|
306
|
+
Ragent.server_logger self, arg
|
307
|
+
end
|
308
|
+
|
309
|
+
def Ragent.server_logger(ra, arg)
|
310
|
+
if ra
|
311
|
+
req = ra.request
|
312
|
+
jid = ra.jid
|
313
|
+
rt = ":R#{(req and req[:id]) ? req[:id] : ra.object_id}"
|
314
|
+
rt += "#{" J:#{jid}" if jid }"
|
315
|
+
end
|
316
|
+
@@logger.info "[RS#{rt}] #{arg}"
|
317
|
+
rescue
|
318
|
+
@@logger.error $!.inspect + "\n" + $!.backtrace.join("\n ")
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module OatsAgent
|
2
|
+
|
3
|
+
class Rclient < EventMachine::Connection
|
4
|
+
attr_reader :response
|
5
|
+
include EM::P::ObjectProtocol
|
6
|
+
# def serializer
|
7
|
+
# YAML
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
def initialize(host, request)
|
11
|
+
@host = host
|
12
|
+
@request = request
|
13
|
+
end
|
14
|
+
|
15
|
+
def receive_object(response)
|
16
|
+
@response = response
|
17
|
+
password = response.delete(:password)
|
18
|
+
length = 500
|
19
|
+
resp = response.inspect
|
20
|
+
resp = resp[0..length] + ' ..... ' + resp[-length..-1] if resp.size > 2*length
|
21
|
+
client_logger "Received " + resp + " from"
|
22
|
+
response[:password] = password
|
23
|
+
client_logger "Command was not recognized" if response[:unknown_command]
|
24
|
+
end
|
25
|
+
|
26
|
+
def client_logger(arg)
|
27
|
+
$log.info arg + " #{@request[:id]}@#{@host} at " + Time.now.strftime("%y-%m-%d %H:%M:%S")
|
28
|
+
end
|
29
|
+
|
30
|
+
def post_init
|
31
|
+
password = @request.delete(:password)
|
32
|
+
client_logger "Sending " + @request.inspect + " to"
|
33
|
+
@request[:password] = password
|
34
|
+
send_object(@request)
|
35
|
+
end
|
36
|
+
def unbind
|
37
|
+
client_logger "Did not hear from " unless @response
|
38
|
+
EventMachine::stop_event_loop
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
if File.directory? oats_dir = File.expand_path('../../../../oats', __FILE__)
|
3
|
+
$:.unshift(oats_dir + '/lib') # To pickup non-gem oats for local debugging
|
4
|
+
else
|
5
|
+
require 'rubygems'
|
6
|
+
gem 'oats'
|
7
|
+
# Below are possibly needed when agent is off machine
|
8
|
+
# if RUBY_PLATFORM =~ /linux/ # Seems to be needed by Ubuntu
|
9
|
+
# gem 'execjs'
|
10
|
+
# gem 'therubyracer'
|
11
|
+
# end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'oats'
|
15
|
+
require "oats/oats_data"
|
16
|
+
require "oats/test_data"
|
17
|
+
require 'oats/roptions'
|
18
|
+
#require 'pp'
|
19
|
+
$:.unshift File.expand_path('..', __FILE__)
|
20
|
+
#pp $:
|
21
|
+
require "ragent"
|
22
|
+
require "rclient"
|
23
|
+
|
24
|
+
options = Oats::Driver.init
|
25
|
+
# $oats_execution['agent'] (used by framework) == Oats.global['agent'] (by YAMLs) === options
|
26
|
+
$oats = Oats::OatsData.load(options['_:ini_file'])
|
27
|
+
$oats['_']['options'] = options
|
28
|
+
Oats::Roptions.override(options)
|
29
|
+
# $oats would be reset and and regenerated by agents across jobs.
|
30
|
+
|
31
|
+
options['execution:occ:agent_host'] = $oats['execution']['occ']['agent_host']
|
32
|
+
options['execution:occ:agent_port'] = $oats['execution']['occ']['agent_port']
|
33
|
+
options['execution:occ:agent_nickname'] = ($oats['execution']['occ']['agent_nickname'] || options['execution:occ:agent_host'].sub(/\..*/,''))
|
34
|
+
$oats['execution']['occ']['agent_nickname'] = options['execution:occ:agent_nickname']
|
35
|
+
|
36
|
+
if ENV['OATS_AGENT_LOGFILE']
|
37
|
+
# For windows always write to console
|
38
|
+
OatsAgent::Ragent.logger.add('console') if RUBY_PLATFORM =~ /(mswin|mingw)/
|
39
|
+
else
|
40
|
+
# Otherwise agent should write to console only when running start.rb directly w/o logfile
|
41
|
+
OatsAgent::Ragent.logger.add('console')
|
42
|
+
archive_dir = File.expand_path "results_archive", ENV['HOME']
|
43
|
+
log_dir = "#{archive_dir}/#{options['execution:occ:agent_nickname']}/agent_logs"
|
44
|
+
ENV['OATS_AGENT_LOGFILE'] = "#{log_dir}/agent_#{Time.new.to_i}.log"
|
45
|
+
end
|
46
|
+
|
47
|
+
Log4r::FileOutputter.new('agent',
|
48
|
+
:filename=>ENV['OATS_AGENT_LOGFILE'], :trunc=>false, :level=>0,
|
49
|
+
:formatter=>Log4r::PatternFormatter.new(:depth=>50,
|
50
|
+
:pattern => "%-5l %d %M", :date_pattern=>"%y-%m-%d %H:%M:%S"))
|
51
|
+
#$log.info "Redirecting output to logfile: " + ENV['OATS_AGENT_LOGFILE']
|
52
|
+
$log.add('agent')
|
53
|
+
OatsAgent::Ragent.logger.add('agent')
|
54
|
+
|
55
|
+
if options['_:command']
|
56
|
+
require 'oats/rclient'
|
57
|
+
options['_:id'] = Time.now.to_i.to_s
|
58
|
+
request = { :command => options['_:command'], :args => options['_:args'] }
|
59
|
+
request[:id] = options['_:id'] if options['_:id']
|
60
|
+
EventMachine::run { EventMachine::connect options['execution:occ:agent_host'],
|
61
|
+
options['execution:occ:agent_port'].to_i, Rclient, options['execution:occ:agent_host'], request
|
62
|
+
}
|
63
|
+
else
|
64
|
+
OatsAgent::Ragent.start $oats['execution']['occ']
|
65
|
+
end
|
data/lib/oats_agent.rb
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
#require 'rubygems'
|
2
|
+
unless ENV['HOSTNAME']
|
3
|
+
if RUBY_PLATFORM =~ /(mswin|mingw)/
|
4
|
+
ENV['HOSTNAME'] = ENV['COMPUTERNAME']
|
5
|
+
else
|
6
|
+
ENV['HOSTNAME'] = `hostname`.chomp
|
7
|
+
end
|
8
|
+
end
|
9
|
+
ENV['HOSTNAME'] = ENV['HOSTNAME'].downcase
|
10
|
+
require 'win32ole' if RUBY_PLATFORM =~ /(mswin|mingw)/
|
11
|
+
|
12
|
+
module OatsAgent
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def fkill(proc)
|
16
|
+
if proc.instance_of? Hash
|
17
|
+
pname = proc['cmd']
|
18
|
+
pid = proc['pid']
|
19
|
+
msg = " PID #{pid}: #{pname}"
|
20
|
+
else
|
21
|
+
pid = proc
|
22
|
+
end
|
23
|
+
$log.warn "Killing" + msg
|
24
|
+
if RUBY_PLATFORM =~ /(mswin|mingw)/
|
25
|
+
killed = Process.kill('KILL',pid.to_i)
|
26
|
+
killed = 0 if RUBY_VERSION =~ /^1.9/ and killed.empty?
|
27
|
+
$log.warn "Failed to kill" + msg if killed == 0
|
28
|
+
else
|
29
|
+
out = `kill -9 #{pid} 2>&1`
|
30
|
+
$log.warn out unless out == ''
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def kill_matching(cmd_string)
|
35
|
+
procs = []
|
36
|
+
if RUBY_PLATFORM =~ /(mswin|mingw)/
|
37
|
+
WIN32OLE.connect("winmgmts://").ExecQuery("select * from win32_process").each do |process|
|
38
|
+
# puts [process.Commandline, process.ProcessId, process.name].inspect
|
39
|
+
procs.push 'pid' => process.ProcessId, 'cmd' => process.CommandLine if process.Commandline =~ /#{cmd_string}/
|
40
|
+
end
|
41
|
+
# Looking for port is too slow on windows
|
42
|
+
# lines = "netstat -#{RUBY_PLATFORM =~ /(mswin|mingw)/ ? 'a' : 'n' } -o"
|
43
|
+
# list = `#{lines}`.split(/\n/)
|
44
|
+
# list.each do |p|
|
45
|
+
# p =~ /:(\d+)\s+.*\s+LISTENING\s+(\d+)/
|
46
|
+
# next unless $1 and $1 == port
|
47
|
+
# procs.push({ 'pid' => $2, 'cmd' => 'LISTENING' })
|
48
|
+
# end
|
49
|
+
else
|
50
|
+
ps_cmd = "ps -ew -o pid=,ppid=,command="
|
51
|
+
ps_list = `#{ps_cmd}`
|
52
|
+
list = ps_list.split(/\n/)
|
53
|
+
list.each do |p|
|
54
|
+
next unless p =~ /#{cmd_string}/
|
55
|
+
p =~ /(\d+)\s+(\d+)\s+(.*)/
|
56
|
+
procs.push({ 'pid' => $1, 'ppid' => $2, 'cmd' => $3 })
|
57
|
+
end
|
58
|
+
end
|
59
|
+
procs.each { |p| fkill(p) }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Initiates process to run agent in the background
|
63
|
+
# Options Hash Keys:
|
64
|
+
# required: agent_nickname, agent_port,
|
65
|
+
# optional: oats_user, test_directory, repository_version
|
66
|
+
|
67
|
+
def spawn(options)
|
68
|
+
agent_host = options["agent_host"] || ENV['HOSTNAME']
|
69
|
+
nick = options["nickname"] || ENV['HOSTNAME']
|
70
|
+
port = options["port"] || 3010
|
71
|
+
port = port.to_s
|
72
|
+
user = options["user"]
|
73
|
+
repo_version = options["repository_version"]
|
74
|
+
dir_tests = options["test_directory"] || ENV['OATS_TESTS']
|
75
|
+
|
76
|
+
archive_dir = File.expand_path "results_archive", ENV['HOME']
|
77
|
+
log_dir = "#{archive_dir}/#{nick}/agent_logs"
|
78
|
+
log_file = "#{log_dir}/agent_#{Time.new.to_i}.log"
|
79
|
+
config_file = "#{log_dir}/config-agent.txt"
|
80
|
+
agent_log_file = "#{log_dir}/agent.log"
|
81
|
+
params = "-n #{nick} -p #{port}"
|
82
|
+
|
83
|
+
FileUtils.mkdir_p(log_dir) unless File.exists?(log_dir)
|
84
|
+
ENV['OATS_AGENT_LOGFILE'] = log_file
|
85
|
+
|
86
|
+
ruby_cmd = File.expand_path('../oats_agent/start.rb', __FILE__) + ' ' + params
|
87
|
+
|
88
|
+
# Need these off when called by OCC, otherwise the OCC values are inherited
|
89
|
+
%w(RUBYOPT BUNDLE_BIN_PATH BUNDLE_GEMFILE).each { |e| ENV[e] = nil }
|
90
|
+
kill_matching ruby_cmd
|
91
|
+
exit if options["kill_agent"]
|
92
|
+
|
93
|
+
if File.directory?(dir_tests + '/.svn') and ENV['OATS_TESTS_SVN_REPOSITORY']
|
94
|
+
svn_out =nil
|
95
|
+
$log.info "Requested OATS Version: #{repo_version}" if repo_version
|
96
|
+
code_version = nil
|
97
|
+
|
98
|
+
3.times do
|
99
|
+
code_version = `svn info #{dir_tests} | sed -n 's/Last Changed Rev: *//p'`
|
100
|
+
code_version = nil if code_version == ''
|
101
|
+
break if code_version.nil? or (repo_version and code_version >= repo_version)
|
102
|
+
cmd = "svn update #{dir_tests} 2>&1"
|
103
|
+
svn_out = `#{cmd}`
|
104
|
+
svn_out.chomp!
|
105
|
+
$log.info svn_out
|
106
|
+
case svn_out
|
107
|
+
when/^At revision/
|
108
|
+
code_version = svn_out.sub(/At revision *(\d+).*/,'\1')
|
109
|
+
when/Cleanup/
|
110
|
+
when/Could not resolve hostname/
|
111
|
+
break
|
112
|
+
end
|
113
|
+
if code_version == ''
|
114
|
+
code_version = nil
|
115
|
+
sleep 3
|
116
|
+
else
|
117
|
+
break
|
118
|
+
end
|
119
|
+
end
|
120
|
+
if svn_out.nil? and ENV['OATS_TESTS_SVN_REPOSITORY']
|
121
|
+
$log.error "Could not update the code version " +( code_version || '') + ( repo_version ? "to #{repo_version}" : '')
|
122
|
+
exit 2
|
123
|
+
end
|
124
|
+
|
125
|
+
elsif ENV["$OATS_TESTS_GIT_REPOSITORY"]
|
126
|
+
dir_tests = archive_dir + ENV['OATS_AGENT_NICKNAME'] + '/oats_tests'
|
127
|
+
`git clone git_rep dir_tests if File.directory?(dir_tests)`
|
128
|
+
Dir.chdir dir_tests
|
129
|
+
origin = ENV["$OATS_TESTS_GIT_REPOSITORY"] || 'origin'
|
130
|
+
|
131
|
+
if repo_version
|
132
|
+
2.times do
|
133
|
+
# may detach HEAD, but it is OK
|
134
|
+
out = `git checkout #{repo_version} 2>&1`
|
135
|
+
break if status == 0
|
136
|
+
if out == "fatal: reference is not a tree: #{repo_version}"
|
137
|
+
$log.info "Need to pull requested version: #{repo_version} "
|
138
|
+
else
|
139
|
+
$log.info "$out"
|
140
|
+
end
|
141
|
+
$log.info `git pull #{origin} master` # fast-forward master from origin
|
142
|
+
end
|
143
|
+
else
|
144
|
+
$log.info `git pull #{origin} master` # fast-forward master from origin
|
145
|
+
end
|
146
|
+
code_version = `git rev-list HEAD -1` # last commit in checked out version
|
147
|
+
if code_version =~ /#{repo_version}/
|
148
|
+
$log.info "Could not update the code version #{code_version} to #{repo_version}"
|
149
|
+
exit 2
|
150
|
+
end
|
151
|
+
$log.info "Using OATS code version: #{code_version}" unless repo_version
|
152
|
+
else
|
153
|
+
code_version = repo_version
|
154
|
+
$log.info "Setting OATS code version to the requested version: #{code_version}" if code_version
|
155
|
+
end if dir_tests
|
156
|
+
ENV['OATS_TESTS_CODE_VERSION'] = code_version
|
157
|
+
|
158
|
+
msg = ''
|
159
|
+
msg += "User: #{user} " if user
|
160
|
+
msg += "Repo version: #{repo_version} " if repo_version
|
161
|
+
$log.info "#{msg}Starting: #{ruby_cmd}"
|
162
|
+
if RUBY_PLATFORM =~ /(mswin|mingw)/
|
163
|
+
archiv = ENV['HOME'] + '/results_archive'
|
164
|
+
cmd = "psexec.exe -d -i -n 10 -w #{archiv} ruby #{ruby_cmd} 2>&1"
|
165
|
+
else
|
166
|
+
cmd = "#{ruby_cmd} >/dev/null 2>&1 &"
|
167
|
+
end
|
168
|
+
out = `#{cmd}`
|
169
|
+
$log.info out unless out == ''
|
170
|
+
File.open(config_file, 'w') {|f| f.puts(nick +' '+ port) }
|
171
|
+
|
172
|
+
10.times do
|
173
|
+
if File.exist? log_file
|
174
|
+
FileUtils.rm_f agent_log_file
|
175
|
+
FileUtils.ln log_file, agent_log_file
|
176
|
+
break
|
177
|
+
end
|
178
|
+
sleep 1
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
main.file=oats_agent/start.rb
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project xmlns="http://www.netbeans.org/ns/project/1">
|
3
|
+
<type>org.netbeans.modules.ruby.rubyproject</type>
|
4
|
+
<configuration>
|
5
|
+
<data xmlns="http://www.netbeans.org/ns/ruby-project/1">
|
6
|
+
<name>oats_agent</name>
|
7
|
+
<source-roots>
|
8
|
+
<root id="src.bin.dir"/>
|
9
|
+
<root id="src.lib.dir"/>
|
10
|
+
</source-roots>
|
11
|
+
<test-roots/>
|
12
|
+
</data>
|
13
|
+
</configuration>
|
14
|
+
</project>
|
data/oats_agent.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/oats_agent/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Levent Atasoy"]
|
6
|
+
gem.email = ["levent.atasoy@gmail.com"]
|
7
|
+
gem.description = %q{With this gem OATS can start in agent mode in the background so that it can communicate with OCC.}
|
8
|
+
gem.summary = gem.description.dup
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "oats_agent"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = OatsAgent::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'oats'
|
19
|
+
gem.add_dependency 'log4r'
|
20
|
+
|
21
|
+
|
22
|
+
gem.add_dependency 'json'
|
23
|
+
gem.add_dependency 'em-http-request'
|
24
|
+
if RUBY_PLATFORM =~ /linux/ # Seems to be needed by Ubuntu
|
25
|
+
gem.add_dependency 'execjs'
|
26
|
+
gem.add_dependency 'therubyracer'
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oats_agent
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 2
|
10
|
+
version: 0.0.2
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Levent Atasoy
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-05-28 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: oats
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: log4r
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: json
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ">="
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
type: :runtime
|
61
|
+
version_requirements: *id003
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: em-http-request
|
64
|
+
prerelease: false
|
65
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
type: :runtime
|
75
|
+
version_requirements: *id004
|
76
|
+
description: With this gem OATS can start in agent mode in the background so that it can communicate with OCC.
|
77
|
+
email:
|
78
|
+
- levent.atasoy@gmail.com
|
79
|
+
executables:
|
80
|
+
- agent.sh~
|
81
|
+
- oats_agent
|
82
|
+
- occ
|
83
|
+
- results_cleanup
|
84
|
+
extensions: []
|
85
|
+
|
86
|
+
extra_rdoc_files: []
|
87
|
+
|
88
|
+
files:
|
89
|
+
- .gitignore
|
90
|
+
- .rvmrc
|
91
|
+
- Gemfile
|
92
|
+
- README.md
|
93
|
+
- Rakefile
|
94
|
+
- bin/agent.sh~
|
95
|
+
- bin/oats_agent
|
96
|
+
- bin/occ
|
97
|
+
- bin/results_cleanup
|
98
|
+
- lib/oats_agent.rb
|
99
|
+
- lib/oats_agent/commandline_options.rb
|
100
|
+
- lib/oats_agent/ragent.rb
|
101
|
+
- lib/oats_agent/rclient.rb
|
102
|
+
- lib/oats_agent/start.rb
|
103
|
+
- lib/oats_agent/version.rb
|
104
|
+
- nbproject/configs/agent_host.properties
|
105
|
+
- nbproject/configs/start.properties
|
106
|
+
- nbproject/project.properties
|
107
|
+
- nbproject/project.xml
|
108
|
+
- oats_agent.gemspec
|
109
|
+
homepage: ""
|
110
|
+
licenses: []
|
111
|
+
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
none: false
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
hash: 3
|
123
|
+
segments:
|
124
|
+
- 0
|
125
|
+
version: "0"
|
126
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
|
+
none: false
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
hash: 3
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
requirements: []
|
136
|
+
|
137
|
+
rubyforge_project:
|
138
|
+
rubygems_version: 1.8.24
|
139
|
+
signing_key:
|
140
|
+
specification_version: 3
|
141
|
+
summary: With this gem OATS can start in agent mode in the background so that it can communicate with OCC.
|
142
|
+
test_files: []
|
143
|
+
|