Commit e9f4d77f authored by Jérome Perrin's avatar Jérome Perrin

Proof of concept to reuse testnode infrastructure to run DREAM simulation scenarios

parent 1c827f20
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_view</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_view</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>view</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Action Information</string> </value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>1.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>View</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/ERP5ProjectUnitTestDistributor_view</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>DREAMSimulationDistributor</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.DREAMSimulationDistributor</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.patches.WorkflowTool"/>
</pickle>
<pickle>
<tuple>
<none/>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</tuple>
</pickle>
</record>
</ZopeData>
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
</portal_type> </portal_type>
<portal_type id="Task Distribution Tool"> <portal_type id="Task Distribution Tool">
<item>Cloud Performance Unit Test Distributor</item> <item>Cloud Performance Unit Test Distributor</item>
<item>DREAM Simulation Distributor</item>
<item>ERP5 Project Unit Test Distributor</item> <item>ERP5 Project Unit Test Distributor</item>
<item>ERP5 Scalability Distributor</item> <item>ERP5 Scalability Distributor</item>
</portal_type> </portal_type>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Base Type" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>content_icon</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>factory</string> </key>
<value> <string>addXMLObject</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>DREAM Simulation Distributor</string> </value>
</item>
<item>
<key> <string>init_script</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>permission</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Base Type</string> </value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>DREAMSimulationDistributor</string> </value>
</item>
<item>
<key> <string>type_interface</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>type_mixin</string> </key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Benchmark Result Line | view Benchmark Result Line | view
Benchmark Result | view Benchmark Result | view
Cloud Performance Unit Test Distributor | view Cloud Performance Unit Test Distributor | view
DREAM Simulation Distributor | view
ERP5 Project Unit Test Distributor | view ERP5 Project Unit Test Distributor | view
ERP5 Scalability Distributor | view ERP5 Scalability Distributor | view
Scalability Test Suite | vcs_repository Scalability Test Suite | vcs_repository
......
document.erp5.TestNode document.erp5.TestNode
document.erp5.TestSuite document.erp5.TestSuite
document.erp5.ERP5ScalabilityDistributor document.erp5.ERP5ScalabilityDistributor
document.erp5.DREAMSimulationDistributor
\ No newline at end of file
Benchmark Result | Benchmark Result Line Benchmark Result | Benchmark Result Line
Scalability Test Suite | Test Suite Repository Scalability Test Suite | Test Suite Repository
Task Distribution Tool | Cloud Performance Unit Test Distributor Task Distribution Tool | Cloud Performance Unit Test Distributor
Task Distribution Tool | DREAM Simulation Distributor
Task Distribution Tool | ERP5 Project Unit Test Distributor Task Distribution Tool | ERP5 Project Unit Test Distributor
Task Distribution Tool | ERP5 Scalability Distributor Task Distribution Tool | ERP5 Scalability Distributor
Test Node Module | Test Node Test Node Module | Test Node
......
Benchmark Result Benchmark Result
Benchmark Result Line Benchmark Result Line
Cloud Performance Unit Test Distributor Cloud Performance Unit Test Distributor
DREAM Simulation Distributor
ERP5 Project Unit Test Distributor ERP5 Project Unit Test Distributor
ERP5 Scalability Distributor ERP5 Scalability Distributor
Scalability Test Suite Scalability Test Suite
......
##############################################################################
#
# Copyright (c) 2014 Nexedi SA and Contributors. All Rights Reserved.
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly advised to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from datetime import datetime,timedelta
import os
import subprocess
import sys
import time
import tempfile
import glob
import SlapOSControler
import json
import time
import shutil
import logging
import string
import random
from ProcessManager import SubprocessError, ProcessManager, CancellationError
from subprocess import CalledProcessError
from NodeTestSuite import SlapOSInstance
from Updater import Updater
from Utils import dealShebang
from erp5.util import taskdistribution
class DREAMSimulationRunner():
def __init__(self, testnode):
self.testnode = testnode
def _getSlapOSControler(self, working_directory):
"""
Create a SlapOSControler for this working dir
"""
return SlapOSControler.SlapOSControler(
working_directory,
self.testnode.config,
self.testnode.log)
def _prepareSlapOS(self, working_directory, slapos_instance, log,
build_software=1, software_path_list=None, **kw):
"""Launch slapos to build software and partitions
XXX: only build & create partition once for DREAM
"""
slapproxy_log = os.path.join(self.testnode.config['log_directory'],
'slapproxy.log')
log('Configured slapproxy log to %r' % slapproxy_log)
reset_software = slapos_instance.retry_software_count > 10
if reset_software:
slapos_instance.retry_software_count = 0
reset_software = False # Never delete ...
log('testnode, retry_software_count : %r' % \
slapos_instance.retry_software_count)
# XXX Create a new controler because working_directory can be
# Different depending of the preparation
slapos_controler = self._getSlapOSControler(working_directory)
slapos_controler.initializeSlapOSControler(slapproxy_log=slapproxy_log,
process_manager=self.testnode.process_manager, reset_software=reset_software,
software_path_list=software_path_list)
self.testnode.process_manager.supervisord_pid_file = os.path.join(\
slapos_controler.instance_root, 'var', 'run', 'supervisord.pid')
# XXX If all software looks already build, we will not run soft again
soft_list = glob.glob(os.path.join(
slapos_instance.working_directory, 'soft', '*'))
completed_soft_list = glob.glob(os.path.join(
slapos_instance.working_directory, 'soft', '*', '.completed'))
if soft_list and len(soft_list) == len(completed_soft_list):
log('testnode: all software seem built, will not build again')
build_software = False
if build_software:
status_dict = slapos_controler.runSoftwareRelease(
self.testnode.config,
environment=self.testnode.config['environment'])
if status_dict['status_code'] != 0:
slapos_instance.retry = True
slapos_instance.retry_software_count += 1
raise SubprocessError(status_dict)
slapos_instance.retry_software_count = 0
status_dict = slapos_controler.runComputerPartition(
self.testnode.config,
environment=self.testnode.config['environment'],
implicit_erp5_config=False)
if status_dict['status_code'] != 0:
slapos_instance.retry = True
slapos_instance.retry_software_count += 1
raise SubprocessError(status_dict)
slapos_instance.retry_software_count = 0
return status_dict
def prepareSlapOSForTestNode(self, test_node_slapos):
"""
We will build slapos software needed by the testnode itself,
like the building of selenium-runner by default
"""
# I don't think we need that
return {}
return self._prepareSlapOS(self.testnode.config['slapos_directory'],
test_node_slapos, self.testnode.log, create_partition=0,
software_path_list=self.testnode.config.get("software_list"))
def prepareSlapOSForTestSuite(self, node_test_suite):
"""
Build softwares needed by testsuites
"""
log = self.testnode.log
if log is None:
log = self.testnode.log
return self._prepareSlapOS(node_test_suite.working_directory,
node_test_suite, log,
software_path_list=[node_test_suite.custom_profile_path])
def runSimulationScenario(self, node_test_suite, portal_url, test_result):
if not node_test_suite.scenario:
return # nothing to do
dream_simulation = glob.glob("%s/inst/*/etc/run/dream_simulation" % \
node_test_suite.working_directory)[0]
with tempfile.NamedTemporaryFile() as tf:
tf.write(json.dumps(json.loads(node_test_suite.scenario)['input']))
tf.flush()
invocation_list = [dream_simulation, tf.name]
status_dict = self.testnode.process_manager.spawn(*invocation_list,
cwd=node_test_suite.working_directory,
log_prefix='dream_simulation', get_output=True)
output = status_dict['stdout']
assert output
self.testnode.log('Posting back result for %s/%s' % (test_result.test_result_path,
node_test_suite.test_result_line_id))
test_result._retryRPC('saveDREAMSimulationResult',
('%s/%s' % (test_result.test_result_path, node_test_suite.test_result_line_id),
output))
def getRelativePathUsage(self):
"""
Used by the method testnode.constructProfile() to know
if the software.cfg have to use relative path or not.
"""
return False
...@@ -350,7 +350,7 @@ class SlapOSControler(object): ...@@ -350,7 +350,7 @@ class SlapOSControler(object):
# so be tolerant and run it a few times before giving up # so be tolerant and run it a few times before giving up
for runs in range(0, MAX_SR_RETRIES): for runs in range(0, MAX_SR_RETRIES):
status_dict = self.spawn(config['slapos_binary'], status_dict = self.spawn(config['slapos_binary'],
'node', 'software', '--all', 'node', 'software', '--all',
'--pidfile', '%s/software.pid' % self.software_root, '--pidfile', '%s/software.pid' % self.software_root,
'--cfg', self.slapos_config, raise_error_if_fail=False, '--cfg', self.slapos_config, raise_error_if_fail=False,
log_prefix='slapgrid_sr', get_output=False) log_prefix='slapgrid_sr', get_output=False)
...@@ -359,25 +359,29 @@ class SlapOSControler(object): ...@@ -359,25 +359,29 @@ class SlapOSControler(object):
return status_dict return status_dict
def runComputerPartition(self, config, environment, def runComputerPartition(self, config, environment,
stdout=None, stderr=None): stdout=None, stderr=None,
implicit_erp5_config=True):
self.log("SlapOSControler.runComputerPartition") self.log("SlapOSControler.runComputerPartition")
# cloudooo-json is required but this is a hack which should be removed # XXX implicit_erp5_config should not be here but passed by classes
config['instance_dict']['cloudooo-json'] = "{}" # depending on it
# report-url, report-project and suite-url are required to seleniumrunner if implicit_erp5_config:
# instance. This is a hack which must be removed. # cloudooo-json is required but this is a hack which should be removed
config['instance_dict']['report-url'] = config.get("report-url", "") config['instance_dict']['cloudooo-json'] = "{}"
config['instance_dict']['report-project'] = config.get("report-project", "") # report-url, report-project and suite-url are required to seleniumrunner
config['instance_dict']['suite-url'] = config.get("suite-url", "") # instance. This is a hack which must be removed.
# XXX: Hack to minimize writes to storage holding MySQL databases. config['instance_dict']['report-url'] = config.get("report-url", "")
# Note this is something we want for all test suites, so it would config['instance_dict']['report-project'] = config.get("report-project", "")
# not be better to define this parameter on each test suite. config['instance_dict']['suite-url'] = config.get("suite-url", "")
# XXX: Also move here the number of test db to create, so that software # XXX: Hack to minimize writes to storage holding MySQL databases.
# release stop create ones by default. # Note this is something we want for all test suites, so it would
config['instance_dict']['_'] = json.dumps({"mariadb": { # not be better to define this parameter on each test suite.
"relaxed-writes": True, # XXX: Also move here the number of test db to create, so that software
"mariadb-relaxed-writes": True, # BBB # release stop create ones by default.
"test-database-amount": 30, config['instance_dict']['_'] = json.dumps({"mariadb": {
}}) "relaxed-writes": True,
"mariadb-relaxed-writes": True, # BBB
"test-database-amount": 30,
}})
for path in self.software_path_list: for path in self.software_path_list:
try: try:
self.slap.registerOpenOrder().request(path, self.slap.registerOpenOrder().request(path,
......
...@@ -46,6 +46,7 @@ from subprocess import CalledProcessError ...@@ -46,6 +46,7 @@ from subprocess import CalledProcessError
from Updater import Updater from Updater import Updater
from NodeTestSuite import NodeTestSuite, SlapOSInstance from NodeTestSuite import NodeTestSuite, SlapOSInstance
from ScalabilityTestRunner import ScalabilityTestRunner from ScalabilityTestRunner import ScalabilityTestRunner
from DREAMSimulationRunner import DREAMSimulationRunner
from UnitTestRunner import UnitTestRunner from UnitTestRunner import UnitTestRunner
from erp5.util import taskdistribution from erp5.util import taskdistribution
...@@ -137,7 +138,6 @@ class TestNode(object): ...@@ -137,7 +138,6 @@ class TestNode(object):
node_test_suite.reference) node_test_suite.reference)
software_config_path = os.path.relpath(software_config_path, from_path) software_config_path = os.path.relpath(software_config_path, from_path)
profile_content_list.append(""" profile_content_list.append("""
[buildout] [buildout]
extends = %(software_config_path)s extends = %(software_config_path)s
...@@ -188,6 +188,10 @@ branch = %(branch)s ...@@ -188,6 +188,10 @@ branch = %(branch)s
sys.path.append(repository_path) sys.path.append(repository_path)
def getAndUpdateFullRevisionList(self, node_test_suite): def getAndUpdateFullRevisionList(self, node_test_suite):
if 0: # already checkout
self.log("no getAndUpdateFullRevisionList ...")
node_test_suite.revision = 0, 0
return []
full_revision_list = [] full_revision_list = []
config = self.config config = self.config
log = self.log log = self.log
...@@ -224,6 +228,9 @@ branch = %(branch)s ...@@ -224,6 +228,9 @@ branch = %(branch)s
return self.suite_log return self.suite_log
def _initializeSuiteLog(self, suite_log_path): def _initializeSuiteLog(self, suite_log_path):
logger = logging.getLogger('testsuite')
self.suite_log = logger.info
return
# remove previous handlers # remove previous handlers
logger = logging.getLogger('testsuite') logger = logging.getLogger('testsuite')
if self.file_handler is not None: if self.file_handler is not None:
...@@ -239,6 +246,9 @@ branch = %(branch)s ...@@ -239,6 +246,9 @@ branch = %(branch)s
self.suite_log = logger.info self.suite_log = logger.info
def checkRevision(self, test_result, node_test_suite): def checkRevision(self, test_result, node_test_suite):
self.log("skipping checkRevision")
return # no check revision
config = self.config config = self.config
log = self.log log = self.log
if log is None: if log is None:
...@@ -349,6 +359,8 @@ from the distributor.") ...@@ -349,6 +359,8 @@ from the distributor.")
runner = UnitTestRunner(self) runner = UnitTestRunner(self)
elif my_test_type == 'ScalabilityTest': elif my_test_type == 'ScalabilityTest':
runner = ScalabilityTestRunner(self) runner = ScalabilityTestRunner(self)
elif my_test_type == 'DREAMSimulation':
runner = DREAMSimulationRunner(self)
else: else:
log("testnode, Runner type %s not implemented.", my_test_type) log("testnode, Runner type %s not implemented.", my_test_type)
raise NotImplementedError raise NotImplementedError
...@@ -371,6 +383,8 @@ from the distributor.") ...@@ -371,6 +383,8 @@ from the distributor.")
runner = UnitTestRunner(node_test_suite) runner = UnitTestRunner(node_test_suite)
elif my_test_type == 'ScalabilityTest': elif my_test_type == 'ScalabilityTest':
runner = ScalabilityTestRunner(node_test_suite) runner = ScalabilityTestRunner(node_test_suite)
elif my_test_type == 'DREAMSimulation':
runner = DREAMSimulationRunner(node_test_suite)
else: else:
log("testnode, Runner type %s not implemented.", my_test_type) log("testnode, Runner type %s not implemented.", my_test_type)
raise NotImplementedError raise NotImplementedError
...@@ -401,7 +415,7 @@ from the distributor.") ...@@ -401,7 +415,7 @@ from the distributor.")
# Give some time so computer partitions may start # Give some time so computer partitions may start
# as partitions can be of any kind we have and likely will never have # as partitions can be of any kind we have and likely will never have
# a reliable way to check if they are up or not ... # a reliable way to check if they are up or not ...
time.sleep(20) time.sleep(1)
if my_test_type == 'UnitTest': if my_test_type == 'UnitTest':
runner.runTestSuite(node_test_suite, portal_url) runner.runTestSuite(node_test_suite, portal_url)
elif my_test_type == 'ScalabilityTest': elif my_test_type == 'ScalabilityTest':
...@@ -422,9 +436,11 @@ from the distributor.") ...@@ -422,9 +436,11 @@ from the distributor.")
) )
self.log(error_message) self.log(error_message)
raise ValueError(error_message) raise ValueError(error_message)
elif my_test_type == 'DREAMSimulation':
runner.runSimulationScenario(node_test_suite, portal_url,
test_result)
else: else:
raise NotImplementedError raise NotImplementedError
# break the loop to get latest priorities from master # break the loop to get latest priorities from master
break break
self.cleanUp(test_result) self.cleanUp(test_result)
...@@ -457,6 +473,7 @@ from the distributor.") ...@@ -457,6 +473,7 @@ from the distributor.")
self.cleanUp(test_result) self.cleanUp(test_result)
if (now-begin) < 120: if (now-begin) < 120:
sleep_time = 120 - (now-begin) sleep_time = 120 - (now-begin)
sleep_time = .1
log("End of processing, going to sleep %s" % sleep_time) log("End of processing, going to sleep %s" % sleep_time)
time.sleep(sleep_time) time.sleep(sleep_time)
except: except:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment