oats_agent 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|