Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
S
slapos.toolbox
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Łukasz Nowak
slapos.toolbox
Commits
63f2e32c
Commit
63f2e32c
authored
Jul 06, 2012
by
Cédric de Saint Martin
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'agent'
parents
daa69dc0
bce547ed
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
496 additions
and
303 deletions
+496
-303
setup.py
setup.py
+2
-3
slapos/agent/agent.py
slapos/agent/agent.py
+494
-170
slapos/agent/report_start.py
slapos/agent/report_start.py
+0
-57
slapos/agent/report_stop.py
slapos/agent/report_stop.py
+0
-73
No files found.
setup.py
View file @
63f2e32c
...
@@ -45,14 +45,13 @@ setup(name=name,
...
@@ -45,14 +45,13 @@ setup(name=name,
extras_require
=
{
extras_require
=
{
'lampconfigure'
:
[
"mysql-python"
],
#needed for MySQL Database access
'lampconfigure'
:
[
"mysql-python"
],
#needed for MySQL Database access
'zodbpack'
:
[
'ZODB3'
],
# needed to play with ZODB
'zodbpack'
:
[
'ZODB3'
],
# needed to play with ZODB
'agent'
:
[
'erp5.util'
],
},
},
zip_safe
=
False
,
# proxy depends on Flask, which has issues with
zip_safe
=
False
,
# proxy depends on Flask, which has issues with
# accessing templates
# accessing templates
entry_points
=
{
entry_points
=
{
'console_scripts'
:
[
'console_scripts'
:
[
'agent = slapos.agent.agent:main'
,
'agent = slapos.agent.agent:main [agent]'
,
'report_start = slapos.agent.report_start:main'
,
'report_stop = slapos.agent.report_stop:main'
,
'clouddestroy = slapos.cloudmgr.destroy:main'
,
'clouddestroy = slapos.cloudmgr.destroy:main'
,
'cloudgetprivatekey = slapos.cloudmgr.getprivatekey:main'
,
'cloudgetprivatekey = slapos.cloudmgr.getprivatekey:main'
,
'cloudgetpubliciplist = slapos.cloudmgr.getpubliciplist:main'
,
'cloudgetpubliciplist = slapos.cloudmgr.getpubliciplist:main'
,
...
...
slapos/agent/agent.py
View file @
63f2e32c
import
ConfigParser
,
argparse
import
ConfigParser
import
argparse
import
httplib
import
json
import
json
from
random
import
random
,
choice
import
logging
import
os
,
socket
,
time
import
os
from
datetime
import
datetime
import
random
from
datetime
import
timedelta
import
sys
import
tempfile
import
traceback
import
time
import
xmlrpclib
import
xmlrpclib
from
logging
import
getLogger
,
basicConfig
import
slapos.slap
from
slapos.slap
import
slap
,
Supply
from
slapos.grid.utils
import
setRunning
,
setFinished
from
slapos.grid.utils
import
setRunning
,
setFinished
from
erp5.util.taskdistribution
import
TaskDistributionTool
,
RPCRetry
def
safeRpcCall
(
proxy
,
function_id
,
*
args
):
class
AutoSTemp
(
object
):
while
True
:
"""
Create a self-destructing temporary file.
Uses mkstemp.
"""
__unlink
=
os
.
unlink
def
__init__
(
self
,
value
):
fd
,
self
.
__name
=
tempfile
.
mkstemp
()
os
.
write
(
fd
,
value
)
os
.
close
(
fd
)
@
property
def
name
(
self
):
return
self
.
__name
def
__del__
(
self
):
self
.
__unlink
(
self
.
__name
)
# XXX: scripts should be merged in a single one
GET_DESTROYING_METHOD_ID
=
\
'Agent_getDestroyingSoftwareReleaseReferenceListOnComputer'
GET_INSTALLED_METHOD_ID
=
\
'Agent_getInstalledSoftwareReleaseReferenceListOnComputer'
GET_INSTALLING_METHOD_ID
=
\
'Agent_getInstallingSoftwareReleaseReferenceListOnComputer'
SOFTWARE_STATE_UNKNOWN
=
-
1
SOFTWARE_STATE_INSTALLING
=
0
SOFTWARE_STATE_INSTALLED
=
1
SOFTWARE_STATE_DESTROYING
=
2
GET_PARTITION_STATE_METHOD_ID
=
'Agent_getComputerPartitionState'
INSTANCE_STATE_UNKNOWN
=
-
1
INSTANCE_STATE_STARTING
=
0
INSTANCE_STATE_STARTED
=
1
INSTANCE_STATE_STOPPING
=
2
INSTANCE_STATE_STOPPED
=
3
INSTANCE_STATE_DESTROYING
=
4
INSTANCE_STATE_DICT
=
{
'Looking for a free partition'
:
INSTANCE_STATE_UNKNOWN
,
'Started'
:
INSTANCE_STATE_STARTED
,
'Start in progress'
:
INSTANCE_STATE_STARTING
,
'Stopped'
:
INSTANCE_STATE_STOPPED
,
'Stop in progress'
:
INSTANCE_STATE_STOPPING
,
'Destruction in progress'
:
INSTANCE_STATE_DESTROYING
,
'Destroyed'
:
INSTANCE_STATE_UNKNOWN
,
}
TESTER_STATE_INITIAL
=
-
1
TESTER_STATE_NOTHING
=
0
TESTER_STATE_SOFTWARE_INSTALLED
=
1
TESTER_STATE_INSTANCE_INSTALLED
=
2
TESTER_STATE_INSTANCE_STARTED
=
4
TESTER_STATE_INSTANCE_UNISTALLED
=
5
class
x509Transport
(
xmlrpclib
.
Transport
):
"""
Similar to xmlrpclib.SecureTransport, but with actually usable x509
support.
"""
def
__init__
(
self
,
x509
,
*
args
,
**
kw
):
xmlrpclib
.
Transport
.
__init__
(
self
,
*
args
,
**
kw
)
self
.
__x509
=
x509
def
make_connection
(
self
,
host
):
if
not
self
.
_connection
or
host
!=
self
.
_connection
[
0
]:
try
:
try
:
function
=
getattr
(
proxy
,
function_id
)
HTTPSConnection
=
httplib
.
HTTPSConnection
return
function
(
*
args
)
except
AttributeError
:
except
(
socket
.
error
,
xmlrpclib
.
ProtocolError
,
xmlrpclib
.
Fault
),
e
:
raise
NotImplementedError
(
"your version of httplib doesn't "
time
.
sleep
(
64
)
"support HTTPS"
)
def
_encode_software_dict
(
software_dict
):
result
=
dict
()
for
key
,
value
in
software_dict
.
items
():
result
[
key
]
=
datetime
.
strftime
(
value
,
"%Y-%m-%dT%H:%M:%S"
)
return
result
def
_decode_software_dict
(
software_dict
):
result
=
dict
()
for
key
,
value
in
software_dict
.
items
():
result
[
key
]
=
datetime
.
strptime
(
value
,
"%Y-%m-%dT%H:%M:%S"
)
return
result
class
Agent
:
def
__init__
(
self
,
configuration_file
):
configuration
=
ConfigParser
.
SafeConfigParser
()
configuration
.
readfp
(
configuration_file
)
self
.
portal_url
=
configuration
.
get
(
"agent"
,
"portal_url"
)
self
.
master_url
=
configuration
.
get
(
"agent"
,
"master_url"
)
self
.
key_file
=
configuration
.
get
(
"agent"
,
"key_file"
)
self
.
cert_file
=
configuration
.
get
(
"agent"
,
"cert_file"
)
self
.
maximum_software_installation_duration
=
\
timedelta
(
minutes
=
configuration
.
getfloat
(
"agent"
,
"maximum_software_installation_duration"
))
self
.
software_live_duration
=
\
timedelta
(
minutes
=
configuration
.
getfloat
(
"agent"
,
"software_live_duration"
))
self
.
computer_list
=
json
.
loads
(
configuration
.
get
(
"agent"
,
"computer_list"
))
self
.
software_list
=
json
.
loads
(
configuration
.
get
(
"agent"
,
"software_list"
))
self
.
software_uri
=
dict
()
for
(
software
,
uri
)
in
configuration
.
items
(
"software_uri"
):
self
.
software_uri
[
software
]
=
uri
self
.
log_directory
=
configuration
.
get
(
"agent"
,
"log_directory"
)
self
.
state_file
=
configuration
.
get
(
"agent"
,
"state_file"
)
filename
=
os
.
path
.
join
(
self
.
log_directory
,
"agent-%s.log"
%
datetime
.
strftime
(
datetime
.
now
(),
"%Y%m%d"
))
basicConfig
(
filename
=
filename
,
format
=
"%(asctime)-15s %(message)s"
,
level
=
"INFO"
)
self
.
logger
=
getLogger
()
self
.
slap
=
slap
()
self
.
slap
.
initializeConnection
(
self
.
master_url
,
self
.
key_file
,
self
.
cert_file
)
self
.
supply
=
Supply
()
state
=
ConfigParser
.
SafeConfigParser
()
state
.
readfp
(
open
(
self
.
state_file
))
self
.
installing_software_dict
=
dict
()
self
.
installed_software_dict
=
dict
()
for
computer
in
self
.
computer_list
:
if
state
.
has_section
(
computer
):
self
.
installing_software_dict
[
computer
]
=
\
_decode_software_dict
(
json
.
loads
(
state
.
get
(
computer
,
"installing_software"
,
"{}"
)))
self
.
installed_software_dict
[
computer
]
=
\
_decode_software_dict
(
json
.
loads
(
state
.
get
(
computer
,
"installed_software"
,
"{}"
)))
else
:
else
:
self
.
installing_software_dict
[
computer
]
=
dict
()
chost
,
self
.
_extra_headers
,
x509
=
self
.
get_host_info
((
host
,
self
.
installed_software_dict
[
computer
]
=
dict
()
self
.
__x509
))
self
.
_connection
=
(
host
,
HTTPSConnection
(
chost
,
None
,
**
x509
))
return
self
.
_connection
[
1
]
def
getDestroyingSoftwareReleaseListOnComputer
(
self
,
computer
):
class
TestTimeout
(
Exception
):
portal
=
xmlrpclib
.
ServerProxy
(
self
.
portal_url
,
allow_none
=
1
)
pass
return
safeRpcCall
(
portal
,
"Agent_getDestroyingSoftwareReleaseReferenceListOnComputer"
,
computer
,
self
.
software_list
)
def
getInstalledSoftwareReleaseListOnComputer
(
self
,
computer
):
class
SoftwareReleaseTester
(
RPCRetry
):
portal
=
xmlrpclib
.
ServerProxy
(
self
.
portal_url
,
allow_none
=
1
)
deadline
=
None
return
safeRpcCall
(
portal
,
"Agent_getInstalledSoftwareReleaseReferenceListOnComputer"
,
computer
,
self
.
software_list
)
latest_state
=
None
def
getInstallingSoftwareReleaseListOnComputer
(
self
,
computer
):
def
__init__
(
self
,
portal
=
xmlrpclib
.
ServerProxy
(
self
.
portal_url
,
allow_none
=
1
)
name
,
return
safeRpcCall
(
portal
,
"Agent_getInstallingSoftwareReleaseReferenceListOnComputer"
,
computer
,
self
.
software_list
)
logger
,
master
,
slap_supply
,
# slapos supply to manage software release
slap_order
,
# slapos open order to manage instance
url
,
# software release url
computer_guid
,
# computer to use for this test run
max_install_duration
,
max_uninstall_duration
,
request_kw
=
None
,
# instance parameters, if instanciation
# testing is desired
max_request_duration
=
None
,
max_destroy_duration
=
None
,
):
super
(
SoftwareReleaseTester
,
self
).
__init__
(
master
,
16
,
logger
)
self
.
name
=
name
self
.
slap_supply
=
slap_supply
self
.
slap_order
=
slap_order
self
.
url
=
url
self
.
computer_guid
=
computer_guid
self
.
request_kw
=
request_kw
self
.
state
=
TESTER_STATE_INITIAL
self
.
transition_dict
=
{
TESTER_STATE_INITIAL
:
(
None
,
None
,
TESTER_STATE_NOTHING
,
None
,
None
,
),
TESTER_STATE_NOTHING
:
(
'install'
,
max_install_duration
,
request_kw
is
None
and
TESTER_STATE_INSTANCE_UNISTALLED
or
\
TESTER_STATE_SOFTWARE_INSTALLED
,
SOFTWARE_STATE_INSTALLED
,
None
,
),
TESTER_STATE_SOFTWARE_INSTALLED
:
(
'request'
,
max_request_duration
,
TESTER_STATE_INSTANCE_STARTED
,
None
,
INSTANCE_STATE_STARTED
,
),
TESTER_STATE_INSTANCE_STARTED
:
(
'destroy'
,
max_destroy_duration
,
TESTER_STATE_INSTANCE_UNISTALLED
,
None
,
INSTANCE_STATE_UNKNOWN
,
),
TESTER_STATE_INSTANCE_UNISTALLED
:
(
'uninstall'
,
max_uninstall_duration
,
None
,
SOFTWARE_STATE_UNKNOWN
,
None
,
),
}
def
getSoftwareReleaseUsageOnComputer
(
self
,
computer
,
software
):
def
__repr__
(
self
):
portal
=
xmlrpclib
.
ServerProxy
(
self
.
portal_url
,
allow_none
=
1
)
deadline
=
self
.
deadline
return
safeRpcCall
(
portal
,
"Agent_getSoftwareReleaseUsageOnComputer"
,
computer
,
software
)
if
deadline
is
not
None
:
deadline
-=
time
.
time
()
deadline
=
'+%is'
%
(
deadline
,
)
return
'<%s(state=%s, deadline=%s) at %x>'
%
(
self
.
__class__
.
__name__
,
self
.
state
,
deadline
,
id
(
self
))
def
requestSoftwareReleaseCleanupOnComputer
(
self
,
computer
,
software
):
def
_supply
(
self
,
state
):
try
:
self
.
_logger
.
info
(
'Supply %s@%s: %s'
,
self
.
url
,
self
.
computer_guid
,
self
.
supply
.
supply
(
self
.
software_uri
[
software
],
computer
,
"destroyed"
)
state
)
self
.
logger
.
info
(
"Successfully requested to cleanup %s on %s."
%
(
software
,
computer
))
return
self
.
slap_supply
.
supply
(
self
.
url
,
self
.
computer_guid
,
state
)
return
True
except
:
def
_request
(
self
,
state
):
self
.
logger
.
info
(
"Failed to request to cleanup %s on %s."
%
(
software
,
computer
))
self
.
_logger
.
info
(
'Request %s@%s: %s'
,
self
.
url
,
self
.
name
,
state
)
return
False
self
.
latest_state
=
state
return
self
.
slap_order
.
request
(
def
requestSoftwareReleaseInstallationOnComputer
(
self
,
computer
,
software
):
software_release
=
self
.
url
,
partition_reference
=
self
.
name
,
state
=
state
,
**
self
.
request_kw
)
def
_getSoftwareState
(
self
):
# TODO: replace with simpler slap-based API
# TODO: merge all 3 entrypoints into a single, to reduce server load.
for
state
,
method_id
in
(
(
SOFTWARE_STATE_DESTROYING
,
GET_DESTROYING_METHOD_ID
),
(
SOFTWARE_STATE_INSTALLED
,
GET_INSTALLED_METHOD_ID
),
(
SOFTWARE_STATE_INSTALLING
,
GET_INSTALLING_METHOD_ID
),
):
if
self
.
url
in
self
.
_retryRPC
(
method_id
,
(
self
.
computer_guid
,
[
self
.
url
])):
return
state
def
_getInstanceState
(
self
):
# TODO: replace with simpler slap-based API
latest_state
=
self
.
latest_state
self
.
_logger
.
debug
(
'latest_state = %r'
,
latest_state
)
if
latest_state
is
None
:
return
INSTANCE_STATE_UNKNOWN
try
:
try
:
self
.
supply
.
supply
(
self
.
software_uri
[
software
],
computer
,
"available"
)
requested
=
self
.
_request
(
latest_state
)
self
.
logger
.
info
(
"Successfully requested to install %s on %s."
%
(
software
,
computer
))
except
slapos
.
slap
.
ServerError
:
return
True
self
.
_logger
.
exception
(
'Got an error requesting partition for '
except
:
'its state'
)
self
.
logger
.
info
(
"Failed to request to install %s on %s."
%
(
software
,
computer
))
return
INSTANCE_STATE_UNKNOWN
return
False
part_id
=
requested
.
getId
()
self
.
_logger
.
debug
(
'part_id = %r'
,
part_id
)
def
writeState
(
self
):
if
not
part_id
:
state
=
ConfigParser
.
SafeConfigParser
()
# Master did not allocate a partition yet.
for
computer
in
self
.
computer_list
:
return
INSTANCE_STATE_UNKNOWN
state
.
add_section
(
computer
)
return
INSTANCE_STATE_DICT
[
self
.
_retryRPC
(
state
.
set
(
computer
,
"installing_software"
,
\
GET_PARTITION_STATE_METHOD_ID
,
json
.
dumps
(
_encode_software_dict
(
self
.
installing_software_dict
[
computer
])))
(
self
.
computer_guid
,
part_id
)
state
.
set
(
computer
,
"installed_software"
,
\
)]
json
.
dumps
(
_encode_software_dict
(
self
.
installed_software_dict
[
computer
])))
state
.
write
(
open
(
self
.
state_file
,
"w"
))
def
install
(
self
):
"""
def
main
(
*
args
):
Make software available on computer.
"""
self
.
_supply
(
'available'
)
def
uninstall
(
self
):
"""
Make software unavailable on computer.
"""
self
.
_supply
(
'destroyed'
)
def
start
(
self
):
"""
Request started instance (or starting existing one)
"""
self
.
_request
(
'started'
)
request
=
start
def
stop
(
self
):
"""
Request stopped instance (or stopping existing one).
"""
self
.
_request
(
'stopped'
)
def
destroy
(
self
):
"""
Destroy existing instance.
"""
self
.
_request
(
'destroyed'
)
def
teardown
(
self
):
"""
Interrupt a running test sequence, putting it in idle state.
"""
if
self
.
request_kw
is
not
None
:
self
.
destroy
()
self
.
uninstall
()
self
.
state
=
TESTER_STATE_INSTANCE_UNISTALLED
def
tic
(
self
,
now
):
"""
Check for missed deadlines (-> test failure), conditions for moving to
next state, and actually moving to next state (executing its payload).
"""
deadline
=
self
.
deadline
if
deadline
<
now
and
deadline
is
not
None
:
raise
TestTimeout
(
self
.
state
)
_
,
_
,
state
,
software_state
,
instance_state
=
self
.
transition_dict
[
self
.
state
]
if
(
software_state
is
None
or
software_state
==
self
.
_getSoftwareState
())
and
(
instance_state
is
None
or
instance_state
==
self
.
_getInstanceState
()):
if
state
is
None
:
return
None
self
.
_logger
.
debug
(
'Going to state %i (%r, %r)'
,
state
,
software_state
,
instance_state
)
self
.
state
=
state
step
,
delay
,
_
,
_
,
_
=
self
.
transition_dict
[
state
]
self
.
deadline
=
now
+
delay
getattr
(
self
,
step
)()
return
self
.
deadline
def
main
():
"""
Note: This code does not test as much as it monitors.
The goal is to regularily try to build & instanciate a software release
on several machines, to monitor vifib stability and SR stability as time
passes (and things once available online become unavailable).
Part of this function could be reused to make an actual test bot, testing
only when actual changes are committed to a software release, to look for
regressions.
Note: This code does not connect to any instanciated service, it relies on
the presence of a promise section to make instanciation fail until promise
is happy.
"""
parser
=
argparse
.
ArgumentParser
()
parser
=
argparse
.
ArgumentParser
()
parser
.
add_argument
(
"--pidfile"
,
help
=
"The location where pidfile will be created."
)
parser
.
add_argument
(
'--pidfile'
,
'-p'
,
help
=
'pidfile preventing parallel '
parser
.
add_argument
(
"configuration_file"
,
nargs
=
1
,
type
=
argparse
.
FileType
(),
'execution.'
)
help
=
"Slap Test Agent configuration file."
)
parser
.
add_argument
(
'--log'
,
'-l'
,
help
=
'Log file path.'
)
if
args
==
():
parser
.
add_argument
(
'--verbose'
,
'-v'
,
help
=
'Be verbose.'
,
argument_option_instance
=
parser
.
parse_args
()
action
=
'store_true'
)
else
:
parser
.
add_argument
(
'configuration_file'
,
type
=
argparse
.
FileType
(),
argument_option_instance
=
\
help
=
'Slap Test Agent configuration file.'
)
parser
.
parse_args
(
list
(
args
))
# Just to keep strong references to AutoSTemp instances
option_dict
=
{}
key_file_dict
=
{}
configuration_file
=
argument_option_instance
.
configuration_file
[
0
]
def
asFilenamePair
(
key
,
cert
):
for
argument_key
,
argument_value
in
vars
(
argument_option_instance
# Note: python's ssl support only supports fetching key & cert data
).
iteritems
():
# from on-disk files. This is why we need to "convert" direct data
option_dict
.
update
({
argument_key
:
argument_value
})
# into file paths, using temporary files.
pidfile
=
option_dict
.
get
(
"pidfile"
,
None
)
cert
=
cert
.
strip
()
try
:
temp_key
,
temp_cert
=
key_file_dict
[
cert
]
except
KeyError
:
temp_key
=
AutoSTemp
(
key
.
strip
())
temp_cert
=
AutoSTemp
(
cert
)
key_file_dict
[
cert
]
=
(
temp_key
,
temp_cert
)
return
temp_key
.
name
,
temp_cert
.
name
args
=
parser
.
parse_args
()
pidfile
=
args
.
pidfile
if
pidfile
:
if
pidfile
:
setRunning
(
pidfile
)
setRunning
(
pidfile
)
try
:
agent
=
Agent
(
configuration_file
)
log
=
args
.
log
now
=
datetime
.
now
()
formatter
=
logging
.
Formatter
(
'%(asctime)s %(message)s'
)
for
computer
in
agent
.
computer_list
:
logger
=
logging
.
getLogger
()
installing_software_list
=
agent
.
getInstallingSoftwareReleaseListOnComputer
(
computer
)
if
args
.
verbose
:
installed_software_list
=
agent
.
getInstalledSoftwareReleaseListOnComputer
(
computer
)
log_level
=
logging
.
DEBUG
destroying_software_list
=
agent
.
getDestroyingSoftwareReleaseListOnComputer
(
computer
)
if
len
(
installing_software_list
)
==
0
:
software
=
choice
(
agent
.
software_list
)
if
software
in
installed_software_list
or
software
in
destroying_software_list
:
pass
else
:
else
:
if
agent
.
requestSoftwareReleaseInstallationOnComputer
(
computer
,
software
):
log_level
=
logging
.
INFO
agent
.
installing_software_dict
[
computer
][
software
]
=
datetime
.
now
()
logger
.
setLevel
(
log_level
)
handler
=
logging
.
StreamHandler
(
sys
.
stdout
)
handler
.
setFormatter
(
formatter
)
logger
.
addHandler
(
handler
)
if
log
:
handler
=
logging
.
FileHandler
(
log
)
handler
.
setFormatter
(
formatter
)
logger
.
addHandler
(
handler
)
log_file
=
open
(
log
)
log_file
.
seek
(
0
,
2
)
section_dict
=
{}
configuration
=
ConfigParser
.
SafeConfigParser
()
configuration
.
readfp
(
args
.
configuration_file
)
for
section
in
configuration
.
sections
():
if
section
==
'agent'
:
continue
section_dict
[
section
]
=
section_entry_dict
=
dict
(
configuration
.
items
(
section
))
for
key
in
(
'request_kw'
,
'max_install_duration'
,
'max_destroy_duration'
,
'max_request_duration'
,
'max_uninstall_duration'
,
'computer_list'
,
):
if
key
in
section_entry_dict
:
section_entry_dict
[
key
]
=
json
.
loads
(
section_entry_dict
[
key
])
if
'key'
in
section_entry_dict
:
key_file
,
cert_file
=
asFilenamePair
(
section_entry_dict
[
'key'
],
section_entry_dict
[
'cert'
])
section_entry_dict
[
'key'
]
=
key_file
section_entry_dict
[
'cert'
]
=
cert_file
agent_parameter_dict
=
dict
(
configuration
.
items
(
'agent'
))
# XXX: should node title be auto-generated by installation recipe ?
# For example, using computer guid.
node_title
=
agent_parameter_dict
[
'node_title'
]
test_title
=
agent_parameter_dict
[
'test_title'
]
project_title
=
agent_parameter_dict
[
'project_title'
]
parallel_task_count
=
int
(
agent_parameter_dict
.
get
(
'task_count'
,
1
))
task_distribution_tool
=
TaskDistributionTool
(
agent_parameter_dict
[
'report_url'
])
master_slap_connection_dict
=
{}
test_result
=
task_distribution_tool
.
createTestResult
(
revision
=
''
,
test_name_list
=
section_dict
.
keys
(),
node_title
=
node_title
,
allow_restart
=
True
,
test_title
=
test_title
,
project_title
=
project_title
,
)
test_result
.
watcher_period
=
60
if
log
:
test_result
.
addWatch
(
log
,
log_file
,
max_history_bytes
=
10000
)
assert
test_result
is
not
None
ran_test_set
=
set
()
running_test_dict
=
{}
more_tests
=
True
while
True
:
# Get up to parallel_task_count tasks to execute
while
len
(
running_test_dict
)
<
parallel_task_count
and
\
more_tests
:
test_line
=
test_result
.
start
(
exclude_list
=
list
(
ran_test_set
))
if
test_line
is
None
:
more_tests
=
False
break
test_name
=
test_line
.
name
try
:
section_entry_dict
=
section_dict
[
test_name
]
except
KeyError
:
# We don't know how to execute this test. Assume it doesn't
# exist anymore, and fail it in result.
test_line
.
stop
(
stderr
=
'This test does not exist on test '
'node %s'
%
(
node_title
,
))
continue
master_url
=
section_entry_dict
[
'master_url'
]
master_slap_connection_key
=
(
master_url
,
section_entry_dict
.
get
(
'key'
))
try
:
supply
,
order
,
rpc
=
master_slap_connection_dict
[
master_slap_connection_key
]
except
KeyError
:
key
=
section_entry_dict
.
get
(
'key'
)
cert
=
section_entry_dict
.
get
(
'cert'
)
slap
=
slapos
.
slap
.
slap
()
slap
.
initializeConnection
(
master_url
,
key
,
cert
)
supply
=
slap
.
registerSupply
()
order
=
slap
.
registerOpenOrder
()
assert
master_url
.
startswith
(
'https:'
)
rpc
=
xmlrpclib
.
ServerProxy
(
master_url
,
allow_none
=
True
,
transport
=
x509Transport
(
{
'key_file'
:
key
,
'cert_file'
:
cert
}))
master_slap_connection_dict
[
master_slap_connection_key
]
=
(
supply
,
order
,
rpc
)
tester
=
SoftwareReleaseTester
(
test_name
+
'_'
+
node_title
+
time
.
strftime
(
'_%Y/%m/%d_%H:%M:%S_+0000'
,
time
.
gmtime
()),
logger
,
rpc
,
supply
,
order
,
section_entry_dict
[
'url'
],
random
.
choice
(
section_entry_dict
[
'computer_list'
]),
section_entry_dict
[
'max_install_duration'
],
section_entry_dict
[
'max_uninstall_duration'
],
section_entry_dict
.
get
(
'request_kw'
),
section_entry_dict
.
get
(
'max_request_duration'
),
section_entry_dict
.
get
(
'max_destroy_duration'
),
)
ran_test_set
.
add
(
test_name
)
running_test_dict
[
test_name
]
=
(
test_line
,
tester
)
if
not
running_test_dict
:
break
now
=
time
.
time
()
# Synchronise refreshes on watcher period, so it doesn't report a
# stalled test node where we are actually still sleeping.
# Change test_result.watcher_period outside this loop if you wish
# to change sleep duration.
next_deadline
=
now
+
test_result
.
watcher_period
for
section
,
(
test_line
,
tester
)
in
running_test_dict
.
items
():
logger
.
info
(
'Checking %s: %r...'
,
section
,
tester
)
try
:
deadline
=
tester
.
tic
(
now
)
except
Exception
:
logger
.
exception
(
'test failed'
)
test_line
.
stop
(
test_count
=
1
,
error_count
=
1
,
failure_count
=
0
,
skip_count
=
0
,
stderr
=
traceback
.
format_exc
(),
)
del
running_test_dict
[
section
]
try
:
tester
.
teardown
()
except
Exception
:
logger
.
exception
(
'teardown failed, human '
'assistance needed for cleanup'
)
raise
else
:
else
:
for
installing_software
in
installing_software_list
:
logger
.
info
(
'%r'
,
tester
)
if
installing_software
in
agent
.
installing_software_dict
[
computer
]:
if
deadline
is
None
:
start_time
=
agent
.
installing_software_dict
[
computer
][
installing_software
]
# TODO: report how long each step took.
if
now
-
start_time
>
agent
.
maximum_software_installation_duration
:
logger
.
info
(
'Finished !'
)
agent
.
logger
.
info
(
"Failed to install %s on %s in %s."
%
\
test_line
.
stop
(
(
installing_software
,
computer
,
agent
.
maximum_software_installation_duration
))
test_count
=
1
,
if
agent
.
requestSoftwareReleaseCleanupOnComputer
(
computer
,
installing_software
):
error_count
=
0
,
del
agent
.
installing_software_dict
[
computer
][
installing_software
]
failure_count
=
0
,
for
installed_software
in
installed_software_list
:
skip_count
=
0
,
if
installed_software
in
agent
.
installing_software_dict
[
computer
]:
)
agent
.
logger
.
info
(
"Successfully installed %s on %s."
%
(
installed_software
,
computer
))
del
running_test_dict
[
section
]
del
agent
.
installing_software_dict
[
computer
][
installed_software
]
else
:
agent
.
installed_software_dict
[
computer
][
installed_software
]
=
now
next_deadline
=
min
(
deadline
,
next_deadline
)
elif
installed_software
in
agent
.
installed_software_dict
[
computer
]
and
\
if
running_test_dict
:
agent
.
getSoftwareReleaseUsageOnComputer
(
computer
,
installed_software
)
==
0
and
\
to_sleep
=
next_deadline
-
time
.
time
()
now
-
agent
.
installed_software_dict
[
computer
][
installed_software
]
>
agent
.
software_live_duration
:
if
to_sleep
>
0
:
if
agent
.
requestSoftwareReleaseCleanupOnComputer
(
computer
,
installed_software
):
logger
.
info
(
'Sleeping %is...'
,
to_sleep
)
del
agent
.
installed_software_dict
[
computer
][
installed_software
]
time
.
sleep
(
to_sleep
)
agent
.
writeState
()
if
not
test_result
.
isAlive
():
for
_
,
tester
in
running_test_dict
.
itervalues
():
tester
.
teardown
()
finally
:
if
pidfile
:
if
pidfile
:
setFinished
(
pidfile
)
setFinished
(
pidfile
)
# Help interpreter get rid of AutoSTemp instances.
key_file_dict
.
clear
()
if
__name__
==
"__main__"
:
if
__name__
==
'__main__'
:
main
()
main
()
slapos/agent/report_start.py
deleted
100644 → 0
View file @
daa69dc0
import
ConfigParser
,
argparse
,
pprint
,
socket
,
sys
,
time
,
xmlrpclib
,
json
def
safeRpcCall
(
function
,
*
args
):
retry
=
64
xmlrpc_arg_list
=
[]
for
argument
in
args
:
if
isinstance
(
argument
,
dict
):
argument
=
dict
([(
x
,
isinstance
(
y
,
str
)
and
xmlrpclib
.
Binary
(
y
)
or
y
)
\
for
(
x
,
y
)
in
argument
.
iteritems
()])
xmlrpc_arg_list
.
append
(
argument
)
while
True
:
try
:
return
function
(
*
xmlrpc_arg_list
)
except
(
socket
.
error
,
xmlrpclib
.
ProtocolError
),
e
:
print
>>
sys
.
stderr
,
e
pprint
.
pprint
(
args
,
file
(
function
.
_Method__name
,
'w'
))
time
.
sleep
(
retry
)
retry
+=
retry
>>
1
def
main
(
*
args
):
parser
=
argparse
.
ArgumentParser
()
parser
.
add_argument
(
"configuration_file"
,
nargs
=
1
,
type
=
argparse
.
FileType
(),
help
=
"Slap Test Agent configuration file."
)
if
args
==
():
argument_option_instance
=
parser
.
parse_args
()
else
:
argument_option_instance
=
\
parser
.
parse_args
(
list
(
args
))
configuration_file
=
argument_option_instance
.
configuration_file
[
0
]
configuration
=
ConfigParser
.
SafeConfigParser
()
configuration
.
readfp
(
configuration_file
)
master_url
=
configuration
.
get
(
"agent"
,
"report_url"
)
if
master_url
[
-
1
]
!=
'/'
:
master_url
+=
'/'
master
=
xmlrpclib
.
ServerProxy
(
"%s%s"
%
(
master_url
,
'portal_task_distribution'
),
allow_none
=
1
)
assert
master
.
getProtocolRevision
()
==
1
software_list
=
json
.
loads
(
configuration
.
get
(
"agent"
,
"software_list"
))
test_result
=
safeRpcCall
(
master
.
createTestResult
,
"SlapOS Test"
,
""
,
software_list
,
True
,
"SlapOS Test"
,
"SlapOS Test Agent"
,
"ViFiB Project"
)
test_result_path
,
revision
=
test_result
state
=
ConfigParser
.
SafeConfigParser
()
state
.
add_section
(
"path"
)
exclude_list
=
software_list
[:]
for
software
in
software_list
:
exclude_list
.
remove
(
software
)
test_path
,
test_name
=
safeRpcCall
(
master
.
startUnitTest
,
test_result_path
,
exclude_list
)
state
.
set
(
"path"
,
test_name
,
test_path
)
exclude_list
.
append
(
software
)
path_file
=
configuration
.
get
(
"agent"
,
"path_file"
)
state
.
write
(
open
(
path_file
,
"w"
))
if
__name__
==
"__main__"
:
main
()
slapos/agent/report_stop.py
deleted
100644 → 0
View file @
daa69dc0
import
ConfigParser
,
argparse
,
pprint
,
socket
,
sys
,
time
,
xmlrpclib
,
json
,
os
from
datetime
import
datetime
def
safeRpcCall
(
function
,
*
args
):
retry
=
64
xmlrpc_arg_list
=
[]
for
argument
in
args
:
if
isinstance
(
argument
,
dict
):
argument
=
dict
([(
x
,
isinstance
(
y
,
str
)
and
xmlrpclib
.
Binary
(
y
)
or
y
)
\
for
(
x
,
y
)
in
argument
.
iteritems
()])
xmlrpc_arg_list
.
append
(
argument
)
while
True
:
try
:
return
function
(
*
xmlrpc_arg_list
)
except
(
socket
.
error
,
xmlrpclib
.
ProtocolError
),
e
:
print
>>
sys
.
stderr
,
e
pprint
.
pprint
(
args
,
file
(
function
.
_Method__name
,
'w'
))
time
.
sleep
(
retry
)
retry
+=
retry
>>
1
def
main
(
*
args
):
parser
=
argparse
.
ArgumentParser
()
parser
.
add_argument
(
"configuration_file"
,
nargs
=
1
,
type
=
argparse
.
FileType
(),
help
=
"Slap Test Agent configuration file."
)
if
args
==
():
argument_option_instance
=
parser
.
parse_args
()
else
:
argument_option_instance
=
\
parser
.
parse_args
(
list
(
args
))
configuration_file
=
argument_option_instance
.
configuration_file
[
0
]
configuration
=
ConfigParser
.
SafeConfigParser
()
configuration
.
readfp
(
configuration_file
)
master_url
=
configuration
.
get
(
"agent"
,
"report_url"
)
software_list
=
json
.
loads
(
configuration
.
get
(
"agent"
,
"software_list"
))
if
master_url
[
-
1
]
!=
'/'
:
master_url
+=
'/'
master
=
xmlrpclib
.
ServerProxy
(
"%s%s"
%
(
master_url
,
'portal_task_distribution'
),
allow_none
=
1
)
assert
master
.
getProtocolRevision
()
==
1
log_directory
=
configuration
.
get
(
"agent"
,
"log_directory"
)
logfile_path
=
os
.
path
.
join
(
log_directory
,
"agent-%s.log"
%
datetime
.
strftime
(
datetime
.
now
(),
"%Y%m%d"
))
logfile
=
open
(
logfile_path
,
'r'
)
logline_list
=
logfile
.
readlines
()
path_file
=
configuration
.
get
(
"agent"
,
"path_file"
)
state
=
ConfigParser
.
SafeConfigParser
()
state
.
readfp
(
open
(
path_file
))
for
software
in
software_list
:
test_path
=
state
.
get
(
"path"
,
software
)
success
,
failure
=
0
,
0
log
=
''
for
logline
in
logline_list
:
success_pattern
=
"Successfully installed %s"
%
software
failure_pattern
=
"Failed to install %s"
%
software
if
success_pattern
in
logline
:
success
=
success
+
1
log
=
log
+
logline
if
failure_pattern
in
logline
:
failure
=
failure
+
1
log
=
log
+
logline
safeRpcCall
(
master
.
stopUnitTest
,
test_path
,
{
"status_code"
:
0
,
"stderr"
:
log
,
"duration"
:
0
,
"test_count"
:
success
+
failure
,
"failure_count"
:
failure
})
if
__name__
==
"__main__"
:
main
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment